diff --git a/.coderabbit.yaml b/.coderabbit.yaml index ec2953a161..822c470cd8 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -8,4 +8,5 @@ reviews: base_branches: - main - 1.6.x - - 1.7.x \ No newline at end of file + - 1.7.x + - 1.8.x \ No newline at end of file diff --git a/.env b/.env index c916cf0db6..da418989bc 100644 --- a/.env +++ b/.env @@ -86,7 +86,7 @@ _APP_COMPUTE_RUNTIMES_NETWORK=runtimes _APP_EXECUTOR_SECRET=your-secret-key _APP_EXECUTOR_HOST=http://exc1/v1 _APP_FUNCTIONS_RUNTIMES=php-8.0,node-18.0,python-3.9,ruby-3.1 -_APP_SITES_RUNTIMES=static-1,node-22,flutter-3.29 +_APP_SITES_RUNTIMES=static-1,node-22,flutter-3.32 _APP_MAINTENANCE_INTERVAL=86400 _APP_MAINTENANCE_START_TIME=12:00 _APP_MAINTENANCE_RETENTION_CACHE=2592000 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7ee1e50abe..c40bca488e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -303,7 +303,7 @@ jobs: docker compose exec -T \ -e _APP_DATABASE_SHARED_TABLES \ -e _APP_DATABASE_SHARED_TABLES_V1 \ - appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude=devKeys + appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys e2e_dev_keys: name: E2E Service Test (Dev Keys) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7746ef99af..277a509447 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -415,8 +415,8 @@ In addition, you will also need to add some logic to the `reduce()` method of th ```php case $document->getCollection() === 'buckets': - $files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES))); - $storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE))); + $files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getSequence(), METRIC_BUCKET_ID_FILES))); + $storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getSequence(), METRIC_BUCKET_ID_FILES_STORAGE))); if (!empty($files['value'])) { $metrics[] = [ @@ -463,9 +463,9 @@ $queueForStatsUsage ->addMetric(METRIC_BUILDS, 1) ->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0)) ->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000) - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS), 1) - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0)) - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000) + ->addMetric(str_replace('{functionInternalId}', $function->getSequence(), METRIC_FUNCTION_ID_BUILDS), 1) + ->addMetric(str_replace('{functionInternalId}', $function->getSequence(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0)) + ->addMetric(str_replace('{functionInternalId}', $function->getSequence(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000) ->setProject($project) ->trigger(); ``` diff --git a/Dockerfile b/Dockerfile index 30b017b573..ac67dc0ab4 100755 --- a/Dockerfile +++ b/Dockerfile @@ -38,9 +38,9 @@ COPY --from=composer /usr/local/src/vendor /usr/src/code/vendor COPY ./app /usr/src/code/app COPY ./public /usr/src/code/public COPY ./bin /usr/local/bin -COPY ./docs /usr/src/code/docs COPY ./src /usr/src/code/src COPY ./dev /usr/src/code/dev +COPY ./vendor /usr/src/code/vendor # Set Volumes RUN mkdir -p /storage/uploads && \ diff --git a/README-CN.md b/README-CN.md index c79e0a1db4..a9a6b3c867 100644 --- a/README-CN.md +++ b/README-CN.md @@ -72,7 +72,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.7.0 + appwrite/appwrite:1.7.4 ``` ### Windows @@ -84,7 +84,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.7.0 + appwrite/appwrite:1.7.4 ``` #### PowerShell @@ -94,7 +94,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.7.0 + appwrite/appwrite:1.7.4 ``` 运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。 diff --git a/README.md b/README.md index 435a2efd87..88d3fe89df 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.7.0 + appwrite/appwrite:1.7.4 ``` ### Windows @@ -90,7 +90,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.7.0 + appwrite/appwrite:1.7.4 ``` #### PowerShell @@ -100,7 +100,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.7.0 + appwrite/appwrite:1.7.4 ``` Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation. diff --git a/app/cli.php b/app/cli.php index a1e41289b2..c5cb7a12c6 100644 --- a/app/cli.php +++ b/app/cli.php @@ -12,11 +12,13 @@ use Appwrite\Runtimes\Runtimes; use Executor\Executor; use Swoole\Runtime; use Swoole\Timer; +use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\CLI; use Utopia\CLI\Console; use Utopia\Config\Config; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; @@ -24,6 +26,7 @@ use Utopia\DSN\DSN; use Utopia\Logger\Log; use Utopia\Platform\Service; use Utopia\Pools\Group; +use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Publisher; use Utopia\Registry\Registry; use Utopia\System\System; @@ -46,10 +49,7 @@ CLI::setResource('cache', function ($pools) { $adapters = []; foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource(); + $adapters[] = new CachePool($pools->get($value)); } return new Cache(new Sharding($adapters)); @@ -69,12 +69,8 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) { $attempts++; try { // Prepare database connection - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource(); - - $dbForPlatform = new Database($dbAdapter, $cache); + $adapter = new DatabasePool($pools->get('console')); + $dbForPlatform = new Database($adapter, $cache); $dbForPlatform ->setNamespace('_console') @@ -92,7 +88,6 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) { $ready = true; } catch (\Throwable $err) { Console::warning($err->getMessage()); - $pools->get('console')->reclaim(); sleep($sleep); } } while ($attempts < $maxAttempts && !$ready); @@ -130,37 +125,33 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform if (\in_array($dsn->getHost(), $sharedTables)) { $database ->setSharedTables(true) - ->setTenant($project->getInternalId()) + ->setTenant($project->getSequence()) ->setNamespace($dsn->getParam('namespace')); } else { $database ->setSharedTables(false) ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); + ->setNamespace('_' . $project->getSequence()); } return $database; } - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); + $adapter = new DatabasePool($pools->get($dsn->getHost())); + $database = new Database($adapter, $cache); $databases[$dsn->getHost()] = $database; $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); if (\in_array($dsn->getHost(), $sharedTables)) { $database ->setSharedTables(true) - ->setTenant($project->getInternalId()) + ->setTenant($project->getSequence()) ->setNamespace($dsn->getParam('namespace')); } else { $database ->setSharedTables(false) ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); + ->setNamespace('_' . $project->getSequence()); } $database @@ -173,21 +164,15 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) { $database = null; + return function (?Document $project = null) use ($pools, $cache, $database) { if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') { - $database->setTenant($project->getInternalId()); + $database->setTenant($project->getSequence()); return $database; } - $dbAdapter = $pools - ->get('logs') - ->pop() - ->getResource(); - - $database = new Database( - $dbAdapter, - $cache - ); + $adapter = new DatabasePool($pools->get('logs')); + $database = new Database($adapter, $cache); $database ->setSharedTables(true) @@ -197,7 +182,7 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) { // set tenant if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') { - $database->setTenant($project->getInternalId()); + $database->setTenant($project->getSequence()); } return $database; @@ -211,7 +196,7 @@ CLI::setResource('queueForStatsResources', function (Publisher $publisher) { return new StatsResources($publisher); }, ['publisher']); CLI::setResource('publisher', function (Group $pools) { - return $pools->get('publisher')->pop()->getResource(); + return new BrokerPool(publisher: $pools->get('publisher')); }, ['pools']); CLI::setResource('queueForFunctions', function (Publisher $publisher) { return new Func($publisher); @@ -266,7 +251,7 @@ CLI::setResource('logError', function (Registry $register) { }; }, ['register']); -CLI::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST'))); +CLI::setResource('executor', fn () => new Executor()); CLI::setResource('telemetry', fn () => new NoTelemetry()); @@ -299,6 +284,5 @@ $cli $cli->shutdown()->action(fn () => Timer::clearAll()); -// Enable coroutines, but disable TCP hooks. These don't work until we use `\Utopia\Cache\Adapter\Pool` and `\Utopia\Database\Adapter\Pool`. -Runtime::enableCoroutine(SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_TCP); +Runtime::enableCoroutine(SWOOLE_HOOK_ALL); run($cli->run(...)); diff --git a/app/config/collections/common.php b/app/config/collections/common.php index e77d5403d2..6de7eb224b 100644 --- a/app/config/collections/common.php +++ b/app/config/collections/common.php @@ -1439,13 +1439,6 @@ return [ 'lengths' => [], 'orders' => [Database::ORDER_ASC], ], - [ - '$id' => ID::custom('_key_roles'), - 'type' => Database::INDEX_KEY, - 'attributes' => ['roles'], - 'lengths' => [128], - 'orders' => [], - ], ], ], diff --git a/app/config/console.php b/app/config/console.php index e37c9b7836..1de3a99370 100644 --- a/app/config/console.php +++ b/app/config/console.php @@ -11,7 +11,7 @@ use Utopia\System\System; $console = [ '$id' => ID::custom('console'), - '$internalId' => ID::custom('console'), + '$sequence' => ID::custom('console'), 'name' => 'Appwrite', '$collection' => ID::custom('projects'), 'description' => 'Appwrite core engine', diff --git a/app/config/errors.php b/app/config/errors.php index 6d9d29a8ea..8365e8c705 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -393,6 +393,16 @@ return [ 'description' => 'Membership is already confirmed.', 'code' => 409, ], + Exception::MEMBERSHIP_DELETION_PROHIBITED => [ + 'name' => Exception::MEMBERSHIP_DELETION_PROHIBITED, + 'description' => 'Membership deletion is prohibited.', + 'code' => 400, + ], + Exception::MEMBERSHIP_DOWNGRADE_PROHIBITED => [ + 'name' => Exception::MEMBERSHIP_DOWNGRADE_PROHIBITED, + 'description' => 'Membership role downgrade is prohibited.', + 'code' => 400, + ], /** Avatars */ Exception::AVATAR_SET_NOT_FOUND => [ diff --git a/app/config/platforms.php b/app/config/platforms.php index ac6c4a6214..f08401e8fa 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -11,7 +11,7 @@ return [ [ 'key' => 'web', 'name' => 'Web', - 'version' => '18.0.0', + 'version' => '18.1.0', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -59,7 +59,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '16.0.0', + 'version' => '17.0.1', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -77,7 +77,7 @@ return [ [ 'key' => 'apple', 'name' => 'Apple', - 'version' => '10.0.0', + 'version' => '10.1.1', 'url' => 'https://github.com/appwrite/sdk-for-apple', 'package' => 'https://github.com/appwrite/sdk-for-apple', 'enabled' => true, @@ -112,7 +112,7 @@ return [ [ 'key' => 'android', 'name' => 'Android', - 'version' => '8.0.0', + 'version' => '8.1.0', 'url' => 'https://github.com/appwrite/sdk-for-android', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android', 'enabled' => true, @@ -134,7 +134,7 @@ return [ [ 'key' => 'react-native', 'name' => 'React Native', - 'version' => '0.9.0', + 'version' => '0.9.1', 'url' => 'https://github.com/appwrite/sdk-for-react-native', 'package' => 'https://npmjs.com/package/react-native-appwrite', 'enabled' => true, @@ -199,7 +199,7 @@ return [ [ 'key' => 'web', 'name' => 'Console', - 'version' => '1.3.0', + 'version' => '1.7.0', 'url' => 'https://github.com/appwrite/sdk-for-console', 'package' => '', 'enabled' => true, @@ -217,7 +217,7 @@ return [ [ 'key' => 'cli', 'name' => 'Command Line', - 'version' => '6.2.3', + 'version' => '8.0.0', 'url' => 'https://github.com/appwrite/sdk-for-cli', 'package' => 'https://www.npmjs.com/package/appwrite-cli', 'enabled' => true, @@ -231,6 +231,11 @@ return [ 'gitRepoName' => 'sdk-for-cli', 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', + 'exclude' => [ + 'services' => [ + ['name' => 'assistant'], + ], + ], ], ], ], @@ -353,7 +358,7 @@ return [ [ 'key' => 'dotnet', 'name' => '.NET', - 'version' => '0.15.0', + 'version' => '0.13.0', 'url' => 'https://github.com/appwrite/sdk-for-dotnet', 'package' => 'https://www.nuget.org/packages/Appwrite', 'enabled' => true, @@ -411,7 +416,7 @@ return [ [ 'key' => 'swift', 'name' => 'Swift', - 'version' => '10.0.0', + 'version' => '10.1.0', 'url' => 'https://github.com/appwrite/sdk-for-swift', 'package' => 'https://github.com/appwrite/sdk-for-swift', 'enabled' => true, diff --git a/app/config/specs/open-api3-1.7.x-client.json b/app/config/specs/open-api3-1.7.x-client.json index 92e64c7ba0..bbb78c6c66 100644 --- a/app/config/specs/open-api3-1.7.x-client.json +++ b/app/config/specs/open-api3-1.7.x-client.json @@ -1,7 +1,7 @@ { "openapi": "3.0.0", "info": { - "version": "1.7.0", + "version": "1.7.4", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -4660,6 +4660,115 @@ } ] }, + "put": { + "summary": "Upsert document", + "operationId": "databasesUpsertDocument", + "tags": [ + "databases" + ], + "description": "Create or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "responses": { + "200": { + "description": "Document", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/document" + } + } + } + } + }, + "x-appwrite": { + "method": "upsertDocument", + "group": "documents", + "weight": 114, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/upsert-document.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/upsert-document.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "client", + "server", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Document data as JSON object. Include all required attributes of the document to be created or updated.", + "x-example": "{}" + }, + "permissions": { + "type": "array", + "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", + "x-example": "[\"read(\"any\")\"]", + "items": { + "type": "string" + } + } + }, + "required": [ + "data" + ] + } + } + } + } + }, "patch": { "summary": "Update document", "operationId": "databasesUpdateDocument", @@ -4781,7 +4890,7 @@ "x-appwrite": { "method": "deleteDocument", "group": "documents", - "weight": 116, + "weight": 117, "cookies": false, "type": "", "deprecated": false, @@ -4865,7 +4974,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 391, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -4940,7 +5049,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 389, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -5055,7 +5164,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 390, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -5129,7 +5238,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 305, + "weight": 306, "cookies": false, "type": "graphql", "deprecated": false, @@ -5181,7 +5290,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 304, + "weight": 305, "cookies": false, "type": "graphql", "deprecated": false, @@ -5233,7 +5342,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 121, + "weight": 122, "cookies": false, "type": "", "deprecated": false, @@ -5285,7 +5394,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 122, + "weight": 123, "cookies": false, "type": "", "deprecated": false, @@ -5337,7 +5446,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 126, + "weight": 127, "cookies": false, "type": "", "deprecated": false, @@ -5389,7 +5498,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 123, + "weight": 124, "cookies": false, "type": "", "deprecated": false, @@ -5441,7 +5550,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 124, + "weight": 125, "cookies": false, "type": "", "deprecated": false, @@ -5493,7 +5602,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 125, + "weight": 126, "cookies": false, "type": "", "deprecated": false, @@ -5545,7 +5654,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 127, + "weight": 128, "cookies": false, "type": "", "deprecated": false, @@ -5597,7 +5706,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 128, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -5649,7 +5758,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 351, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -5732,7 +5841,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 355, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -5807,7 +5916,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 211, + "weight": 212, "cookies": false, "type": "", "deprecated": false, @@ -5893,7 +6002,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 210, + "weight": 211, "cookies": false, "type": "upload", "deprecated": false, @@ -5991,7 +6100,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 212, + "weight": 213, "cookies": false, "type": "", "deprecated": false, @@ -6063,7 +6172,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 217, + "weight": 218, "cookies": false, "type": "", "deprecated": false, @@ -6152,7 +6261,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 218, + "weight": 219, "cookies": false, "type": "", "deprecated": false, @@ -6219,7 +6328,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 214, + "weight": 215, "cookies": false, "type": "location", "deprecated": false, @@ -6297,7 +6406,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 213, + "weight": 214, "cookies": false, "type": "location", "deprecated": false, @@ -6524,7 +6633,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 215, + "weight": 216, "cookies": false, "type": "location", "deprecated": false, @@ -6609,7 +6718,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 222, + "weight": 223, "cookies": false, "type": "", "deprecated": false, @@ -6685,7 +6794,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 221, + "weight": 222, "cookies": false, "type": "", "deprecated": false, @@ -6770,7 +6879,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 223, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -6832,7 +6941,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 225, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -6906,7 +7015,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 227, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -6970,7 +7079,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 229, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -7056,7 +7165,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 228, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -7167,7 +7276,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 230, + "weight": 231, "cookies": false, "type": "", "deprecated": false, @@ -7239,7 +7348,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 231, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -7326,7 +7435,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 233, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -7400,7 +7509,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 232, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -7498,7 +7607,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 224, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -7559,7 +7668,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 226, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -9752,6 +9861,12 @@ "name": "X-Appwrite-Session", "description": "The user session to authenticate with", "in": "header" + }, + "DevKey": { + "type": "apiKey", + "name": "X-Appwrite-Dev-Key", + "description": "Your secret dev API key", + "in": "header" } } }, diff --git a/app/config/specs/open-api3-1.7.x-console.json b/app/config/specs/open-api3-1.7.x-console.json index 31d5016e85..90ef137fc2 100644 --- a/app/config/specs/open-api3-1.7.x-console.json +++ b/app/config/specs/open-api3-1.7.x-console.json @@ -1,7 +1,7 @@ { "openapi": "3.0.0", "info": { - "version": "1.7.0", + "version": "1.7.4", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -4359,7 +4359,7 @@ "x-appwrite": { "method": "chat", "group": "console", - "weight": 307, + "weight": 308, "cookies": false, "type": "", "deprecated": false, @@ -4419,7 +4419,7 @@ "x-appwrite": { "method": "getResource", "group": null, - "weight": 431, + "weight": 432, "cookies": false, "type": "", "deprecated": false, @@ -4494,7 +4494,7 @@ "x-appwrite": { "method": "variables", "group": "console", - "weight": 306, + "weight": 307, "cookies": false, "type": "", "deprecated": false, @@ -4694,7 +4694,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 118, + "weight": 119, "cookies": false, "type": "", "deprecated": false, @@ -8167,7 +8167,7 @@ "x-appwrite": { "method": "upsertDocuments", "group": "documents", - "weight": 115, + "weight": 116, "cookies": false, "type": "", "deprecated": false, @@ -8227,7 +8227,10 @@ "type": "object" } } - } + }, + "required": [ + "documents" + ] } } } @@ -8255,7 +8258,7 @@ "x-appwrite": { "method": "updateDocuments", "group": "documents", - "weight": 114, + "weight": 115, "cookies": false, "type": "", "deprecated": false, @@ -8348,7 +8351,7 @@ "x-appwrite": { "method": "deleteDocuments", "group": "documents", - "weight": 117, + "weight": 118, "cookies": false, "type": "", "deprecated": false, @@ -8511,6 +8514,115 @@ } ] }, + "put": { + "summary": "Upsert document", + "operationId": "databasesUpsertDocument", + "tags": [ + "databases" + ], + "description": "Create or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "responses": { + "200": { + "description": "Document", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/document" + } + } + } + } + }, + "x-appwrite": { + "method": "upsertDocument", + "group": "documents", + "weight": 114, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/upsert-document.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/upsert-document.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "client", + "server", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Document data as JSON object. Include all required attributes of the document to be created or updated.", + "x-example": "{}" + }, + "permissions": { + "type": "array", + "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", + "x-example": "[\"read(\"any\")\"]", + "items": { + "type": "string" + } + } + }, + "required": [ + "data" + ] + } + } + } + } + }, "patch": { "summary": "Update document", "operationId": "databasesUpdateDocument", @@ -8632,7 +8744,7 @@ "x-appwrite": { "method": "deleteDocument", "group": "documents", - "weight": 116, + "weight": 117, "cookies": false, "type": "", "deprecated": false, @@ -9253,7 +9365,7 @@ "x-appwrite": { "method": "getCollectionUsage", "group": null, - "weight": 120, + "weight": 121, "cookies": false, "type": "", "deprecated": false, @@ -9418,7 +9530,7 @@ "x-appwrite": { "method": "getDatabaseUsage", "group": null, - "weight": 119, + "weight": 120, "cookies": false, "type": "", "deprecated": false, @@ -9500,7 +9612,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 375, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -9573,7 +9685,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 372, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -9804,7 +9916,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 377, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -9853,7 +9965,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 378, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -9903,7 +10015,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 401, + "weight": 402, "cookies": false, "type": "", "deprecated": false, @@ -10003,7 +10115,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 400, + "weight": 401, "cookies": false, "type": "", "deprecated": false, @@ -10063,7 +10175,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 394, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -10135,7 +10247,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 373, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -10194,7 +10306,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 374, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -10422,7 +10534,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 376, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -10483,7 +10595,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 381, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -10563,7 +10675,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 382, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -10646,7 +10758,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 379, + "weight": 380, "cookies": false, "type": "upload", "deprecated": false, @@ -10742,7 +10854,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 387, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -10827,7 +10939,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 384, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -10930,7 +11042,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 385, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -11027,7 +11139,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 380, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -11089,7 +11201,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 383, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -11153,7 +11265,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 386, + "weight": 387, "cookies": false, "type": "location", "deprecated": false, @@ -11243,7 +11355,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 388, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -11314,7 +11426,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 391, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -11389,7 +11501,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 389, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -11504,7 +11616,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 390, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -11569,7 +11681,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 392, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -11640,7 +11752,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 393, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -11722,7 +11834,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 397, + "weight": 398, "cookies": false, "type": "", "deprecated": false, @@ -11781,7 +11893,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 395, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -11872,7 +11984,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 396, + "weight": 397, "cookies": false, "type": "", "deprecated": false, @@ -11941,7 +12053,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 398, + "weight": 399, "cookies": false, "type": "", "deprecated": false, @@ -12032,7 +12144,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 399, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -12103,7 +12215,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 305, + "weight": 306, "cookies": false, "type": "graphql", "deprecated": false, @@ -12155,7 +12267,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 304, + "weight": 305, "cookies": false, "type": "graphql", "deprecated": false, @@ -12207,7 +12319,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 129, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -12256,7 +12368,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 150, + "weight": 151, "cookies": false, "type": "", "deprecated": false, @@ -12305,7 +12417,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 132, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -12354,7 +12466,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 137, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -12414,7 +12526,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 131, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -12463,7 +12575,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 133, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -12512,7 +12624,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 139, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -12574,7 +12686,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 138, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -12636,7 +12748,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 140, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -12709,7 +12821,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 141, + "weight": 142, "cookies": false, "type": "", "deprecated": false, @@ -12771,7 +12883,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 151, + "weight": 152, "cookies": false, "type": "", "deprecated": false, @@ -12859,7 +12971,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 145, + "weight": 146, "cookies": false, "type": "", "deprecated": false, @@ -12921,7 +13033,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 136, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -12983,7 +13095,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 142, + "weight": 143, "cookies": false, "type": "", "deprecated": false, @@ -13045,7 +13157,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 143, + "weight": 144, "cookies": false, "type": "", "deprecated": false, @@ -13107,7 +13219,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 144, + "weight": 145, "cookies": false, "type": "", "deprecated": false, @@ -13169,7 +13281,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 146, + "weight": 147, "cookies": false, "type": "", "deprecated": false, @@ -13231,7 +13343,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 147, + "weight": 148, "cookies": false, "type": "", "deprecated": false, @@ -13293,7 +13405,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 135, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -13355,7 +13467,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 149, + "weight": 150, "cookies": false, "type": "", "deprecated": false, @@ -13404,7 +13516,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 148, + "weight": 149, "cookies": false, "type": "", "deprecated": false, @@ -13453,7 +13565,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 134, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -13502,7 +13614,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 121, + "weight": 122, "cookies": false, "type": "", "deprecated": false, @@ -13554,7 +13666,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 122, + "weight": 123, "cookies": false, "type": "", "deprecated": false, @@ -13606,7 +13718,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 126, + "weight": 127, "cookies": false, "type": "", "deprecated": false, @@ -13658,7 +13770,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 123, + "weight": 124, "cookies": false, "type": "", "deprecated": false, @@ -13710,7 +13822,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 124, + "weight": 125, "cookies": false, "type": "", "deprecated": false, @@ -13762,7 +13874,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 125, + "weight": 126, "cookies": false, "type": "", "deprecated": false, @@ -13814,7 +13926,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 127, + "weight": 128, "cookies": false, "type": "", "deprecated": false, @@ -13866,7 +13978,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 128, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -13918,7 +14030,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 359, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -13994,7 +14106,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 356, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -14138,7 +14250,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 363, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -14284,7 +14396,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 358, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -14458,7 +14570,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 365, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -14636,7 +14748,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 357, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -14745,7 +14857,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 364, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -14857,7 +14969,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 362, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -14910,7 +15022,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 366, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -14972,7 +15084,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 360, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -15047,7 +15159,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 361, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -15122,7 +15234,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 331, + "weight": 332, "cookies": false, "type": "", "deprecated": false, @@ -15198,7 +15310,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 330, + "weight": 331, "cookies": false, "type": "", "deprecated": false, @@ -15303,7 +15415,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 343, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -15411,7 +15523,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 329, + "weight": 330, "cookies": false, "type": "", "deprecated": false, @@ -15496,7 +15608,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 342, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -15584,7 +15696,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 321, + "weight": 322, "cookies": false, "type": "", "deprecated": false, @@ -15699,7 +15811,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 334, + "weight": 335, "cookies": false, "type": "", "deprecated": false, @@ -15817,7 +15929,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 324, + "weight": 325, "cookies": false, "type": "", "deprecated": false, @@ -15912,7 +16024,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 337, + "weight": 338, "cookies": false, "type": "", "deprecated": false, @@ -16010,7 +16122,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 322, + "weight": 323, "cookies": false, "type": "", "deprecated": false, @@ -16115,7 +16227,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 335, + "weight": 336, "cookies": false, "type": "", "deprecated": false, @@ -16223,7 +16335,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 323, + "weight": 324, "cookies": false, "type": "", "deprecated": false, @@ -16366,7 +16478,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 336, + "weight": 337, "cookies": false, "type": "", "deprecated": false, @@ -16511,7 +16623,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 325, + "weight": 326, "cookies": false, "type": "", "deprecated": false, @@ -16606,7 +16718,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 338, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -16704,7 +16816,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 326, + "weight": 327, "cookies": false, "type": "", "deprecated": false, @@ -16799,7 +16911,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 339, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -16897,7 +17009,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 327, + "weight": 328, "cookies": false, "type": "", "deprecated": false, @@ -16992,7 +17104,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 340, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -17090,7 +17202,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 328, + "weight": 329, "cookies": false, "type": "", "deprecated": false, @@ -17185,7 +17297,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 341, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -17283,7 +17395,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 333, + "weight": 334, "cookies": false, "type": "", "deprecated": false, @@ -17336,7 +17448,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 344, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -17398,7 +17510,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 332, + "weight": 333, "cookies": false, "type": "", "deprecated": false, @@ -17473,7 +17585,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 353, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -17548,7 +17660,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 346, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -17622,7 +17734,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 345, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -17705,7 +17817,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 348, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -17765,7 +17877,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 349, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -17842,7 +17954,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 350, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -17904,7 +18016,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 347, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -17979,7 +18091,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 352, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -18063,7 +18175,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 351, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -18153,7 +18265,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 354, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -18216,7 +18328,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 355, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -18291,7 +18403,7 @@ "x-appwrite": { "method": "list", "group": null, - "weight": 313, + "weight": 314, "cookies": false, "type": "", "deprecated": false, @@ -18365,7 +18477,7 @@ "x-appwrite": { "method": "createAppwriteMigration", "group": null, - "weight": 308, + "weight": 309, "cookies": false, "type": "", "deprecated": false, @@ -18453,7 +18565,7 @@ "x-appwrite": { "method": "getAppwriteReport", "group": null, - "weight": 315, + "weight": 316, "cookies": false, "type": "", "deprecated": false, @@ -18546,7 +18658,7 @@ "x-appwrite": { "method": "createCsvMigration", "group": null, - "weight": 312, + "weight": 313, "cookies": false, "type": "", "deprecated": false, @@ -18625,7 +18737,7 @@ "x-appwrite": { "method": "createFirebaseMigration", "group": null, - "weight": 309, + "weight": 310, "cookies": false, "type": "", "deprecated": false, @@ -18701,7 +18813,7 @@ "x-appwrite": { "method": "getFirebaseReport", "group": null, - "weight": 316, + "weight": 317, "cookies": false, "type": "", "deprecated": false, @@ -18773,7 +18885,7 @@ "x-appwrite": { "method": "createNHostMigration", "group": null, - "weight": 311, + "weight": 312, "cookies": false, "type": "", "deprecated": false, @@ -18884,7 +18996,7 @@ "x-appwrite": { "method": "getNHostReport", "group": null, - "weight": 318, + "weight": 319, "cookies": false, "type": "", "deprecated": false, @@ -19017,7 +19129,7 @@ "x-appwrite": { "method": "createSupabaseMigration", "group": null, - "weight": 310, + "weight": 311, "cookies": false, "type": "", "deprecated": false, @@ -19122,7 +19234,7 @@ "x-appwrite": { "method": "getSupabaseReport", "group": null, - "weight": 317, + "weight": 318, "cookies": false, "type": "", "deprecated": false, @@ -19246,7 +19358,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 314, + "weight": 315, "cookies": false, "type": "", "deprecated": false, @@ -19304,7 +19416,7 @@ "x-appwrite": { "method": "retry", "group": null, - "weight": 319, + "weight": 320, "cookies": false, "type": "", "deprecated": false, @@ -19355,7 +19467,7 @@ "x-appwrite": { "method": "delete", "group": null, - "weight": 320, + "weight": 321, "cookies": false, "type": "", "deprecated": false, @@ -19415,7 +19527,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 199, + "weight": 200, "cookies": false, "type": "", "deprecated": false, @@ -19503,7 +19615,7 @@ "x-appwrite": { "method": "listVariables", "group": null, - "weight": 201, + "weight": 202, "cookies": false, "type": "", "deprecated": false, @@ -19549,7 +19661,7 @@ "x-appwrite": { "method": "createVariable", "group": null, - "weight": 200, + "weight": 201, "cookies": false, "type": "", "deprecated": false, @@ -19627,7 +19739,7 @@ "x-appwrite": { "method": "getVariable", "group": null, - "weight": 202, + "weight": 203, "cookies": false, "type": "", "deprecated": false, @@ -19685,7 +19797,7 @@ "x-appwrite": { "method": "updateVariable", "group": null, - "weight": 203, + "weight": 204, "cookies": false, "type": "", "deprecated": false, @@ -19765,7 +19877,7 @@ "x-appwrite": { "method": "deleteVariable", "group": null, - "weight": 204, + "weight": 205, "cookies": false, "type": "", "deprecated": false, @@ -19825,7 +19937,7 @@ "x-appwrite": { "method": "list", "group": "projects", - "weight": 154, + "weight": 155, "cookies": false, "type": "", "deprecated": false, @@ -19897,7 +20009,7 @@ "x-appwrite": { "method": "create", "group": "projects", - "weight": 153, + "weight": 154, "cookies": false, "type": "", "deprecated": false, @@ -20031,7 +20143,7 @@ "x-appwrite": { "method": "get", "group": "projects", - "weight": 155, + "weight": 156, "cookies": false, "type": "", "deprecated": false, @@ -20089,7 +20201,7 @@ "x-appwrite": { "method": "update", "group": "projects", - "weight": 156, + "weight": 157, "cookies": false, "type": "", "deprecated": false, @@ -20204,7 +20316,7 @@ "x-appwrite": { "method": "delete", "group": "projects", - "weight": 173, + "weight": 174, "cookies": false, "type": "", "deprecated": false, @@ -20264,7 +20376,7 @@ "x-appwrite": { "method": "updateApiStatus", "group": "projects", - "weight": 160, + "weight": 161, "cookies": false, "type": "", "deprecated": false, @@ -20356,7 +20468,7 @@ "x-appwrite": { "method": "updateApiStatusAll", "group": "projects", - "weight": 161, + "weight": 162, "cookies": false, "type": "", "deprecated": false, @@ -20435,7 +20547,7 @@ "x-appwrite": { "method": "updateAuthDuration", "group": "auth", - "weight": 166, + "weight": 167, "cookies": false, "type": "", "deprecated": false, @@ -20514,7 +20626,7 @@ "x-appwrite": { "method": "updateAuthLimit", "group": "auth", - "weight": 165, + "weight": 166, "cookies": false, "type": "", "deprecated": false, @@ -20593,7 +20705,7 @@ "x-appwrite": { "method": "updateAuthSessionsLimit", "group": "auth", - "weight": 171, + "weight": 172, "cookies": false, "type": "", "deprecated": false, @@ -20672,7 +20784,7 @@ "x-appwrite": { "method": "updateMembershipsPrivacy", "group": "auth", - "weight": 164, + "weight": 165, "cookies": false, "type": "", "deprecated": false, @@ -20763,7 +20875,7 @@ "x-appwrite": { "method": "updateMockNumbers", "group": "auth", - "weight": 172, + "weight": 173, "cookies": false, "type": "", "deprecated": false, @@ -20845,7 +20957,7 @@ "x-appwrite": { "method": "updateAuthPasswordDictionary", "group": "auth", - "weight": 169, + "weight": 170, "cookies": false, "type": "", "deprecated": false, @@ -20924,7 +21036,7 @@ "x-appwrite": { "method": "updateAuthPasswordHistory", "group": "auth", - "weight": 168, + "weight": 169, "cookies": false, "type": "", "deprecated": false, @@ -21003,7 +21115,7 @@ "x-appwrite": { "method": "updatePersonalDataCheck", "group": "auth", - "weight": 170, + "weight": 171, "cookies": false, "type": "", "deprecated": false, @@ -21082,7 +21194,7 @@ "x-appwrite": { "method": "updateSessionAlerts", "group": "auth", - "weight": 163, + "weight": 164, "cookies": false, "type": "", "deprecated": false, @@ -21161,7 +21273,7 @@ "x-appwrite": { "method": "updateAuthStatus", "group": "auth", - "weight": 167, + "weight": 168, "cookies": false, "type": "", "deprecated": false, @@ -21261,7 +21373,7 @@ "x-appwrite": { "method": "listDevKeys", "group": "devKeys", - "weight": 370, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -21329,7 +21441,7 @@ "x-appwrite": { "method": "createDevKey", "group": "devKeys", - "weight": 367, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -21414,7 +21526,7 @@ "x-appwrite": { "method": "getDevKey", "group": "devKeys", - "weight": 369, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -21482,7 +21594,7 @@ "x-appwrite": { "method": "updateDevKey", "group": "devKeys", - "weight": 368, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -21568,7 +21680,7 @@ "x-appwrite": { "method": "deleteDevKey", "group": "devKeys", - "weight": 371, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -21638,7 +21750,7 @@ "x-appwrite": { "method": "createJWT", "group": "auth", - "weight": 185, + "weight": 186, "cookies": false, "type": "", "deprecated": false, @@ -21725,7 +21837,7 @@ "x-appwrite": { "method": "listKeys", "group": "keys", - "weight": 181, + "weight": 182, "cookies": false, "type": "", "deprecated": false, @@ -21783,7 +21895,7 @@ "x-appwrite": { "method": "createKey", "group": "keys", - "weight": 180, + "weight": 181, "cookies": false, "type": "", "deprecated": false, @@ -21876,7 +21988,7 @@ "x-appwrite": { "method": "getKey", "group": "keys", - "weight": 182, + "weight": 183, "cookies": false, "type": "", "deprecated": false, @@ -21944,7 +22056,7 @@ "x-appwrite": { "method": "updateKey", "group": "keys", - "weight": 183, + "weight": 184, "cookies": false, "type": "", "deprecated": false, @@ -22038,7 +22150,7 @@ "x-appwrite": { "method": "deleteKey", "group": "keys", - "weight": 184, + "weight": 185, "cookies": false, "type": "", "deprecated": false, @@ -22108,7 +22220,7 @@ "x-appwrite": { "method": "updateOAuth2", "group": "auth", - "weight": 162, + "weight": 163, "cookies": false, "type": "", "deprecated": false, @@ -22246,7 +22358,7 @@ "x-appwrite": { "method": "listPlatforms", "group": "platforms", - "weight": 187, + "weight": 188, "cookies": false, "type": "", "deprecated": false, @@ -22304,7 +22416,7 @@ "x-appwrite": { "method": "createPlatform", "group": "platforms", - "weight": 186, + "weight": 187, "cookies": false, "type": "", "deprecated": false, @@ -22423,7 +22535,7 @@ "x-appwrite": { "method": "getPlatform", "group": "platforms", - "weight": 188, + "weight": 189, "cookies": false, "type": "", "deprecated": false, @@ -22491,7 +22603,7 @@ "x-appwrite": { "method": "updatePlatform", "group": "platforms", - "weight": 189, + "weight": 190, "cookies": false, "type": "", "deprecated": false, @@ -22586,7 +22698,7 @@ "x-appwrite": { "method": "deletePlatform", "group": "platforms", - "weight": 190, + "weight": 191, "cookies": false, "type": "", "deprecated": false, @@ -22656,7 +22768,7 @@ "x-appwrite": { "method": "updateServiceStatus", "group": "projects", - "weight": 158, + "weight": 159, "cookies": false, "type": "", "deprecated": false, @@ -22757,7 +22869,7 @@ "x-appwrite": { "method": "updateServiceStatusAll", "group": "projects", - "weight": 159, + "weight": 160, "cookies": false, "type": "", "deprecated": false, @@ -22836,7 +22948,7 @@ "x-appwrite": { "method": "updateSmtp", "group": "templates", - "weight": 191, + "weight": 192, "cookies": false, "type": "", "deprecated": false, @@ -22954,7 +23066,7 @@ "x-appwrite": { "method": "createSmtpTest", "group": "templates", - "weight": 192, + "weight": 193, "cookies": false, "type": "", "deprecated": false, @@ -23085,7 +23197,7 @@ "x-appwrite": { "method": "updateTeam", "group": "projects", - "weight": 157, + "weight": 158, "cookies": false, "type": "", "deprecated": false, @@ -23164,7 +23276,7 @@ "x-appwrite": { "method": "getEmailTemplate", "group": "templates", - "weight": 194, + "weight": 195, "cookies": false, "type": "", "deprecated": false, @@ -23388,7 +23500,7 @@ "x-appwrite": { "method": "updateEmailTemplate", "group": "templates", - "weight": 196, + "weight": 197, "cookies": false, "type": "", "deprecated": false, @@ -23652,7 +23764,7 @@ "x-appwrite": { "method": "deleteEmailTemplate", "group": "templates", - "weight": 198, + "weight": 199, "cookies": false, "type": "", "deprecated": false, @@ -23878,7 +23990,7 @@ "x-appwrite": { "method": "getSmsTemplate", "group": "templates", - "weight": 193, + "weight": 194, "cookies": false, "type": "", "deprecated": false, @@ -24099,7 +24211,7 @@ "x-appwrite": { "method": "updateSmsTemplate", "group": "templates", - "weight": 195, + "weight": 196, "cookies": false, "type": "", "deprecated": false, @@ -24339,7 +24451,7 @@ "x-appwrite": { "method": "deleteSmsTemplate", "group": "templates", - "weight": 197, + "weight": 198, "cookies": false, "type": "", "deprecated": false, @@ -24562,7 +24674,7 @@ "x-appwrite": { "method": "listWebhooks", "group": "webhooks", - "weight": 175, + "weight": 176, "cookies": false, "type": "", "deprecated": false, @@ -24620,7 +24732,7 @@ "x-appwrite": { "method": "createWebhook", "group": "webhooks", - "weight": 174, + "weight": 175, "cookies": false, "type": "", "deprecated": false, @@ -24735,7 +24847,7 @@ "x-appwrite": { "method": "getWebhook", "group": "webhooks", - "weight": 176, + "weight": 177, "cookies": false, "type": "", "deprecated": false, @@ -24803,7 +24915,7 @@ "x-appwrite": { "method": "updateWebhook", "group": "webhooks", - "weight": 177, + "weight": 178, "cookies": false, "type": "", "deprecated": false, @@ -24919,7 +25031,7 @@ "x-appwrite": { "method": "deleteWebhook", "group": "webhooks", - "weight": 179, + "weight": 180, "cookies": false, "type": "", "deprecated": false, @@ -24989,7 +25101,7 @@ "x-appwrite": { "method": "updateWebhookSignature", "group": "webhooks", - "weight": 178, + "weight": 179, "cookies": false, "type": "", "deprecated": false, @@ -25059,7 +25171,7 @@ "x-appwrite": { "method": "listRules", "group": null, - "weight": 291, + "weight": 292, "cookies": false, "type": "", "deprecated": false, @@ -25133,7 +25245,7 @@ "x-appwrite": { "method": "createAPIRule", "group": null, - "weight": 432, + "weight": 433, "cookies": false, "type": "", "deprecated": false, @@ -25200,7 +25312,7 @@ "x-appwrite": { "method": "createFunctionRule", "group": null, - "weight": 434, + "weight": 435, "cookies": false, "type": "", "deprecated": false, @@ -25278,7 +25390,7 @@ "x-appwrite": { "method": "createRedirectRule", "group": null, - "weight": 435, + "weight": 436, "cookies": false, "type": "", "deprecated": false, @@ -25370,7 +25482,7 @@ "x-appwrite": { "method": "createSiteRule", "group": null, - "weight": 433, + "weight": 434, "cookies": false, "type": "", "deprecated": false, @@ -25448,7 +25560,7 @@ "x-appwrite": { "method": "getRule", "group": null, - "weight": 292, + "weight": 293, "cookies": false, "type": "", "deprecated": false, @@ -25499,7 +25611,7 @@ "x-appwrite": { "method": "deleteRule", "group": null, - "weight": 293, + "weight": 294, "cookies": false, "type": "", "deprecated": false, @@ -25559,7 +25671,7 @@ "x-appwrite": { "method": "updateRuleVerification", "group": null, - "weight": 294, + "weight": 295, "cookies": false, "type": "", "deprecated": false, @@ -25619,7 +25731,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 404, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -25689,7 +25801,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 402, + "weight": 403, "cookies": false, "type": "", "deprecated": false, @@ -25936,7 +26048,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 407, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -25985,7 +26097,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 430, + "weight": 431, "cookies": false, "type": "", "deprecated": false, @@ -26035,7 +26147,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 426, + "weight": 427, "cookies": false, "type": "", "deprecated": false, @@ -26135,7 +26247,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 427, + "weight": 428, "cookies": false, "type": "", "deprecated": false, @@ -26195,7 +26307,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 428, + "weight": 429, "cookies": false, "type": "", "deprecated": false, @@ -26267,7 +26379,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 403, + "weight": 404, "cookies": false, "type": "", "deprecated": false, @@ -26326,7 +26438,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 405, + "weight": 406, "cookies": false, "type": "", "deprecated": false, @@ -26569,7 +26681,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 406, + "weight": 407, "cookies": false, "type": "", "deprecated": false, @@ -26630,7 +26742,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 413, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -26710,7 +26822,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 412, + "weight": 413, "cookies": false, "type": "", "deprecated": false, @@ -26793,7 +26905,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 408, + "weight": 409, "cookies": false, "type": "upload", "deprecated": false, @@ -26894,7 +27006,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 416, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -26974,7 +27086,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 409, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -27077,7 +27189,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 410, + "weight": 411, "cookies": false, "type": "", "deprecated": false, @@ -27175,7 +27287,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 411, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -27237,7 +27349,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 414, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -27301,7 +27413,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 415, + "weight": 416, "cookies": false, "type": "location", "deprecated": false, @@ -27391,7 +27503,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 417, + "weight": 418, "cookies": false, "type": "", "deprecated": false, @@ -27462,7 +27574,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 419, + "weight": 420, "cookies": false, "type": "", "deprecated": false, @@ -27533,7 +27645,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 418, + "weight": 419, "cookies": false, "type": "", "deprecated": false, @@ -27595,7 +27707,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 420, + "weight": 421, "cookies": false, "type": "", "deprecated": false, @@ -27666,7 +27778,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 429, + "weight": 430, "cookies": false, "type": "", "deprecated": false, @@ -27748,7 +27860,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 423, + "weight": 424, "cookies": false, "type": "", "deprecated": false, @@ -27807,7 +27919,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 421, + "weight": 422, "cookies": false, "type": "", "deprecated": false, @@ -27898,7 +28010,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 422, + "weight": 423, "cookies": false, "type": "", "deprecated": false, @@ -27967,7 +28079,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 424, + "weight": 425, "cookies": false, "type": "", "deprecated": false, @@ -28058,7 +28170,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 425, + "weight": 426, "cookies": false, "type": "", "deprecated": false, @@ -28129,7 +28241,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 206, + "weight": 207, "cookies": false, "type": "", "deprecated": false, @@ -28202,7 +28314,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 205, + "weight": 206, "cookies": false, "type": "", "deprecated": false, @@ -28329,7 +28441,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 207, + "weight": 208, "cookies": false, "type": "", "deprecated": false, @@ -28388,7 +28500,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 208, + "weight": 209, "cookies": false, "type": "", "deprecated": false, @@ -28512,7 +28624,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 209, + "weight": 210, "cookies": false, "type": "", "deprecated": false, @@ -28573,7 +28685,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 211, + "weight": 212, "cookies": false, "type": "", "deprecated": false, @@ -28659,7 +28771,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 210, + "weight": 211, "cookies": false, "type": "upload", "deprecated": false, @@ -28757,7 +28869,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 212, + "weight": 213, "cookies": false, "type": "", "deprecated": false, @@ -28829,7 +28941,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 217, + "weight": 218, "cookies": false, "type": "", "deprecated": false, @@ -28918,7 +29030,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 218, + "weight": 219, "cookies": false, "type": "", "deprecated": false, @@ -28985,7 +29097,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 214, + "weight": 215, "cookies": false, "type": "location", "deprecated": false, @@ -29063,7 +29175,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 213, + "weight": 214, "cookies": false, "type": "location", "deprecated": false, @@ -29290,7 +29402,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 215, + "weight": 216, "cookies": false, "type": "location", "deprecated": false, @@ -29375,7 +29487,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 219, + "weight": 220, "cookies": false, "type": "", "deprecated": false, @@ -29447,7 +29559,7 @@ "x-appwrite": { "method": "getBucketUsage", "group": null, - "weight": 220, + "weight": 221, "cookies": false, "type": "", "deprecated": false, @@ -29529,7 +29641,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 222, + "weight": 223, "cookies": false, "type": "", "deprecated": false, @@ -29605,7 +29717,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 221, + "weight": 222, "cookies": false, "type": "", "deprecated": false, @@ -29690,7 +29802,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 223, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -29752,7 +29864,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 225, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -29826,7 +29938,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 227, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -29890,7 +30002,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 234, + "weight": 235, "cookies": false, "type": "", "deprecated": false, @@ -29963,7 +30075,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 229, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -30049,7 +30161,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 228, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -30160,7 +30272,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 230, + "weight": 231, "cookies": false, "type": "", "deprecated": false, @@ -30232,7 +30344,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 231, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -30319,7 +30431,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 233, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -30393,7 +30505,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 232, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -30490,7 +30602,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 224, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -30550,7 +30662,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 226, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -30631,7 +30743,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 438, + "weight": 439, "cookies": false, "type": "", "deprecated": false, @@ -30695,7 +30807,7 @@ "tags": [ "tokens" ], - "description": "Create a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "description": "Create a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "responses": { "201": { "description": "ResourceToken", @@ -30711,12 +30823,12 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 436, + "weight": 437, "cookies": false, "type": "", "deprecated": false, "demo": "tokens\/create-file-token.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "rate-limit": 60, "rate-time": 60, "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", @@ -30800,7 +30912,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 437, + "weight": 438, "cookies": false, "type": "", "deprecated": false, @@ -30860,7 +30972,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 439, + "weight": 440, "cookies": false, "type": "", "deprecated": false, @@ -30930,7 +31042,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 440, + "weight": 441, "cookies": false, "type": "", "deprecated": false, @@ -30992,7 +31104,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 244, + "weight": 245, "cookies": false, "type": "", "deprecated": false, @@ -31065,7 +31177,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 235, + "weight": 236, "cookies": false, "type": "", "deprecated": false, @@ -31153,7 +31265,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 238, + "weight": 239, "cookies": false, "type": "", "deprecated": false, @@ -31238,7 +31350,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 236, + "weight": 237, "cookies": false, "type": "", "deprecated": false, @@ -31323,7 +31435,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 252, + "weight": 253, "cookies": false, "type": "", "deprecated": false, @@ -31391,7 +31503,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 275, + "weight": 276, "cookies": false, "type": "", "deprecated": false, @@ -31452,7 +31564,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 237, + "weight": 238, "cookies": false, "type": "", "deprecated": false, @@ -31537,7 +31649,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 240, + "weight": 241, "cookies": false, "type": "", "deprecated": false, @@ -31622,7 +31734,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 241, + "weight": 242, "cookies": false, "type": "", "deprecated": false, @@ -31737,7 +31849,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 242, + "weight": 243, "cookies": false, "type": "", "deprecated": false, @@ -31840,7 +31952,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 239, + "weight": 240, "cookies": false, "type": "", "deprecated": false, @@ -31945,7 +32057,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 277, + "weight": 278, "cookies": false, "type": "", "deprecated": false, @@ -32017,7 +32129,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 245, + "weight": 246, "cookies": false, "type": "", "deprecated": false, @@ -32069,7 +32181,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 273, + "weight": 274, "cookies": false, "type": "", "deprecated": false, @@ -32130,7 +32242,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 258, + "weight": 259, "cookies": false, "type": "", "deprecated": false, @@ -32210,7 +32322,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 276, + "weight": 277, "cookies": false, "type": "", "deprecated": false, @@ -32292,7 +32404,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 254, + "weight": 255, "cookies": false, "type": "", "deprecated": false, @@ -32375,7 +32487,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 250, + "weight": 251, "cookies": false, "type": "", "deprecated": false, @@ -32449,7 +32561,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 249, + "weight": 250, "cookies": false, "type": "", "deprecated": false, @@ -32534,7 +32646,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 263, + "weight": 264, "cookies": false, "type": "", "deprecated": false, @@ -32607,7 +32719,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 268, + "weight": 269, "cookies": false, "type": "", "deprecated": false, @@ -32683,7 +32795,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 264, + "weight": 265, "cookies": false, "type": "", "deprecated": false, @@ -32744,7 +32856,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 265, + "weight": 266, "cookies": false, "type": "", "deprecated": false, @@ -32803,7 +32915,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 267, + "weight": 268, "cookies": false, "type": "", "deprecated": false, @@ -32862,7 +32974,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 266, + "weight": 267, "cookies": false, "type": "", "deprecated": false, @@ -32923,7 +33035,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 256, + "weight": 257, "cookies": false, "type": "", "deprecated": false, @@ -33003,7 +33115,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 257, + "weight": 258, "cookies": false, "type": "", "deprecated": false, @@ -33083,7 +33195,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 259, + "weight": 260, "cookies": false, "type": "", "deprecated": false, @@ -33163,7 +33275,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 246, + "weight": 247, "cookies": false, "type": "", "deprecated": false, @@ -33222,7 +33334,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 261, + "weight": 262, "cookies": false, "type": "", "deprecated": false, @@ -33302,7 +33414,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 248, + "weight": 249, "cookies": false, "type": "", "deprecated": false, @@ -33361,7 +33473,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 269, + "weight": 270, "cookies": false, "type": "", "deprecated": false, @@ -33413,7 +33525,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 272, + "weight": 273, "cookies": false, "type": "", "deprecated": false, @@ -33467,7 +33579,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 271, + "weight": 272, "cookies": false, "type": "", "deprecated": false, @@ -33538,7 +33650,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 253, + "weight": 254, "cookies": false, "type": "", "deprecated": false, @@ -33618,7 +33730,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 251, + "weight": 252, "cookies": false, "type": "", "deprecated": false, @@ -33691,7 +33803,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 243, + "weight": 244, "cookies": false, "type": "", "deprecated": false, @@ -33801,7 +33913,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 247, + "weight": 248, "cookies": false, "type": "", "deprecated": false, @@ -33871,7 +33983,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 262, + "weight": 263, "cookies": false, "type": "", "deprecated": false, @@ -33960,7 +34072,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 274, + "weight": 275, "cookies": false, "type": "", "deprecated": false, @@ -34032,7 +34144,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 270, + "weight": 271, "cookies": false, "type": "", "deprecated": false, @@ -34114,7 +34226,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 260, + "weight": 261, "cookies": false, "type": "", "deprecated": false, @@ -34194,7 +34306,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 255, + "weight": 256, "cookies": false, "type": "", "deprecated": false, @@ -34274,7 +34386,7 @@ "x-appwrite": { "method": "createRepositoryDetection", "group": "repositories", - "weight": 281, + "weight": 282, "cookies": false, "type": "", "deprecated": false, @@ -34370,7 +34482,7 @@ "x-appwrite": { "method": "listRepositories", "group": "repositories", - "weight": 282, + "weight": 283, "cookies": false, "type": "", "deprecated": false, @@ -34455,7 +34567,7 @@ "x-appwrite": { "method": "createRepository", "group": "repositories", - "weight": 283, + "weight": 284, "cookies": false, "type": "", "deprecated": false, @@ -34540,7 +34652,7 @@ "x-appwrite": { "method": "getRepository", "group": "repositories", - "weight": 284, + "weight": 285, "cookies": false, "type": "", "deprecated": false, @@ -34610,7 +34722,7 @@ "x-appwrite": { "method": "listRepositoryBranches", "group": "repositories", - "weight": 285, + "weight": 286, "cookies": false, "type": "", "deprecated": false, @@ -34680,7 +34792,7 @@ "x-appwrite": { "method": "getRepositoryContents", "group": "repositories", - "weight": 280, + "weight": 281, "cookies": false, "type": "", "deprecated": false, @@ -34754,7 +34866,7 @@ "x-appwrite": { "method": "updateExternalDeployments", "group": "repositories", - "weight": 290, + "weight": 291, "cookies": false, "type": "", "deprecated": false, @@ -34843,7 +34955,7 @@ "x-appwrite": { "method": "listInstallations", "group": "installations", - "weight": 287, + "weight": 288, "cookies": false, "type": "", "deprecated": false, @@ -34917,7 +35029,7 @@ "x-appwrite": { "method": "getInstallation", "group": "installations", - "weight": 288, + "weight": 289, "cookies": false, "type": "", "deprecated": false, @@ -34968,7 +35080,7 @@ "x-appwrite": { "method": "deleteInstallation", "group": "installations", - "weight": 289, + "weight": 290, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/open-api3-1.7.x-server.json b/app/config/specs/open-api3-1.7.x-server.json index ac1c26d5b6..1ae9328864 100644 --- a/app/config/specs/open-api3-1.7.x-server.json +++ b/app/config/specs/open-api3-1.7.x-server.json @@ -1,7 +1,7 @@ { "openapi": "3.0.0", "info": { - "version": "1.7.0", + "version": "1.7.4", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -7650,7 +7650,7 @@ "x-appwrite": { "method": "upsertDocuments", "group": "documents", - "weight": 115, + "weight": 116, "cookies": false, "type": "", "deprecated": false, @@ -7711,7 +7711,10 @@ "type": "object" } } - } + }, + "required": [ + "documents" + ] } } } @@ -7739,7 +7742,7 @@ "x-appwrite": { "method": "updateDocuments", "group": "documents", - "weight": 114, + "weight": 115, "cookies": false, "type": "", "deprecated": false, @@ -7833,7 +7836,7 @@ "x-appwrite": { "method": "deleteDocuments", "group": "documents", - "weight": 117, + "weight": 118, "cookies": false, "type": "", "deprecated": false, @@ -7999,6 +8002,117 @@ } ] }, + "put": { + "summary": "Upsert document", + "operationId": "databasesUpsertDocument", + "tags": [ + "databases" + ], + "description": "Create or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "responses": { + "200": { + "description": "Document", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/document" + } + } + } + } + }, + "x-appwrite": { + "method": "upsertDocument", + "group": "documents", + "weight": 114, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/upsert-document.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/upsert-document.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "client", + "server", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Session": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Document data as JSON object. Include all required attributes of the document to be created or updated.", + "x-example": "{}" + }, + "permissions": { + "type": "array", + "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", + "x-example": "[\"read(\"any\")\"]", + "items": { + "type": "string" + } + } + }, + "required": [ + "data" + ] + } + } + } + } + }, "patch": { "summary": "Update document", "operationId": "databasesUpdateDocument", @@ -8122,7 +8236,7 @@ "x-appwrite": { "method": "deleteDocument", "group": "documents", - "weight": 116, + "weight": 117, "cookies": false, "type": "", "deprecated": false, @@ -8573,7 +8687,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 375, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -8647,7 +8761,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 372, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -8879,7 +8993,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 377, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -8929,7 +9043,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 378, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -8980,7 +9094,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 373, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -9040,7 +9154,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 374, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -9269,7 +9383,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 376, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -9331,7 +9445,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 381, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -9412,7 +9526,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 382, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -9496,7 +9610,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 379, + "weight": 380, "cookies": false, "type": "upload", "deprecated": false, @@ -9593,7 +9707,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 387, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -9679,7 +9793,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 384, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -9783,7 +9897,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 385, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -9881,7 +9995,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 380, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -9944,7 +10058,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 383, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -10009,7 +10123,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 386, + "weight": 387, "cookies": false, "type": "location", "deprecated": false, @@ -10100,7 +10214,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 388, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -10172,7 +10286,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 391, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -10249,7 +10363,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 389, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -10366,7 +10480,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 390, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -10433,7 +10547,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 392, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -10505,7 +10619,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 397, + "weight": 398, "cookies": false, "type": "", "deprecated": false, @@ -10565,7 +10679,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 395, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -10657,7 +10771,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 396, + "weight": 397, "cookies": false, "type": "", "deprecated": false, @@ -10727,7 +10841,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 398, + "weight": 399, "cookies": false, "type": "", "deprecated": false, @@ -10819,7 +10933,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 399, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -10891,7 +11005,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 305, + "weight": 306, "cookies": false, "type": "graphql", "deprecated": false, @@ -10945,7 +11059,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 304, + "weight": 305, "cookies": false, "type": "graphql", "deprecated": false, @@ -10999,7 +11113,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 129, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -11049,7 +11163,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 150, + "weight": 151, "cookies": false, "type": "", "deprecated": false, @@ -11099,7 +11213,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 132, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -11149,7 +11263,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 137, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -11210,7 +11324,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 131, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -11260,7 +11374,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 133, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -11310,7 +11424,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 139, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -11373,7 +11487,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 138, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -11436,7 +11550,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 140, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -11510,7 +11624,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 141, + "weight": 142, "cookies": false, "type": "", "deprecated": false, @@ -11573,7 +11687,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 151, + "weight": 152, "cookies": false, "type": "", "deprecated": false, @@ -11662,7 +11776,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 145, + "weight": 146, "cookies": false, "type": "", "deprecated": false, @@ -11725,7 +11839,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 136, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -11788,7 +11902,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 142, + "weight": 143, "cookies": false, "type": "", "deprecated": false, @@ -11851,7 +11965,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 143, + "weight": 144, "cookies": false, "type": "", "deprecated": false, @@ -11914,7 +12028,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 144, + "weight": 145, "cookies": false, "type": "", "deprecated": false, @@ -11977,7 +12091,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 146, + "weight": 147, "cookies": false, "type": "", "deprecated": false, @@ -12040,7 +12154,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 147, + "weight": 148, "cookies": false, "type": "", "deprecated": false, @@ -12103,7 +12217,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 135, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -12166,7 +12280,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 149, + "weight": 150, "cookies": false, "type": "", "deprecated": false, @@ -12216,7 +12330,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 148, + "weight": 149, "cookies": false, "type": "", "deprecated": false, @@ -12266,7 +12380,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 134, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -12316,7 +12430,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 121, + "weight": 122, "cookies": false, "type": "", "deprecated": false, @@ -12370,7 +12484,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 122, + "weight": 123, "cookies": false, "type": "", "deprecated": false, @@ -12424,7 +12538,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 126, + "weight": 127, "cookies": false, "type": "", "deprecated": false, @@ -12478,7 +12592,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 123, + "weight": 124, "cookies": false, "type": "", "deprecated": false, @@ -12532,7 +12646,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 124, + "weight": 125, "cookies": false, "type": "", "deprecated": false, @@ -12586,7 +12700,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 125, + "weight": 126, "cookies": false, "type": "", "deprecated": false, @@ -12640,7 +12754,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 127, + "weight": 128, "cookies": false, "type": "", "deprecated": false, @@ -12694,7 +12808,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 128, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -12748,7 +12862,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 359, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -12825,7 +12939,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 356, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -12970,7 +13084,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 363, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -13117,7 +13231,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 358, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -13292,7 +13406,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 365, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -13471,7 +13585,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 357, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -13581,7 +13695,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 364, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -13694,7 +13808,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 362, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -13748,7 +13862,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 366, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -13811,7 +13925,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 360, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -13887,7 +14001,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 361, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -13963,7 +14077,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 331, + "weight": 332, "cookies": false, "type": "", "deprecated": false, @@ -14040,7 +14154,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 330, + "weight": 331, "cookies": false, "type": "", "deprecated": false, @@ -14146,7 +14260,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 343, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -14255,7 +14369,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 329, + "weight": 330, "cookies": false, "type": "", "deprecated": false, @@ -14341,7 +14455,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 342, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -14430,7 +14544,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 321, + "weight": 322, "cookies": false, "type": "", "deprecated": false, @@ -14546,7 +14660,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 334, + "weight": 335, "cookies": false, "type": "", "deprecated": false, @@ -14665,7 +14779,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 324, + "weight": 325, "cookies": false, "type": "", "deprecated": false, @@ -14761,7 +14875,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 337, + "weight": 338, "cookies": false, "type": "", "deprecated": false, @@ -14860,7 +14974,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 322, + "weight": 323, "cookies": false, "type": "", "deprecated": false, @@ -14966,7 +15080,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 335, + "weight": 336, "cookies": false, "type": "", "deprecated": false, @@ -15075,7 +15189,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 323, + "weight": 324, "cookies": false, "type": "", "deprecated": false, @@ -15219,7 +15333,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 336, + "weight": 337, "cookies": false, "type": "", "deprecated": false, @@ -15365,7 +15479,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 325, + "weight": 326, "cookies": false, "type": "", "deprecated": false, @@ -15461,7 +15575,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 338, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -15560,7 +15674,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 326, + "weight": 327, "cookies": false, "type": "", "deprecated": false, @@ -15656,7 +15770,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 339, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -15755,7 +15869,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 327, + "weight": 328, "cookies": false, "type": "", "deprecated": false, @@ -15851,7 +15965,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 340, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -15950,7 +16064,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 328, + "weight": 329, "cookies": false, "type": "", "deprecated": false, @@ -16046,7 +16160,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 341, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -16145,7 +16259,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 333, + "weight": 334, "cookies": false, "type": "", "deprecated": false, @@ -16199,7 +16313,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 344, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -16262,7 +16376,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 332, + "weight": 333, "cookies": false, "type": "", "deprecated": false, @@ -16338,7 +16452,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 353, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -16414,7 +16528,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 346, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -16489,7 +16603,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 345, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -16573,7 +16687,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 348, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -16634,7 +16748,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 349, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -16712,7 +16826,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 350, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -16775,7 +16889,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 347, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -16851,7 +16965,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 352, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -16936,7 +17050,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 351, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -17028,7 +17142,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 354, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -17092,7 +17206,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 355, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -17169,7 +17283,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 404, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -17240,7 +17354,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 402, + "weight": 403, "cookies": false, "type": "", "deprecated": false, @@ -17488,7 +17602,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 407, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -17538,7 +17652,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 430, + "weight": 431, "cookies": false, "type": "", "deprecated": false, @@ -17589,7 +17703,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 403, + "weight": 404, "cookies": false, "type": "", "deprecated": false, @@ -17649,7 +17763,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 405, + "weight": 406, "cookies": false, "type": "", "deprecated": false, @@ -17893,7 +18007,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 406, + "weight": 407, "cookies": false, "type": "", "deprecated": false, @@ -17955,7 +18069,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 413, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -18036,7 +18150,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 412, + "weight": 413, "cookies": false, "type": "", "deprecated": false, @@ -18120,7 +18234,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 408, + "weight": 409, "cookies": false, "type": "upload", "deprecated": false, @@ -18222,7 +18336,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 416, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -18303,7 +18417,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 409, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -18407,7 +18521,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 410, + "weight": 411, "cookies": false, "type": "", "deprecated": false, @@ -18506,7 +18620,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 411, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -18569,7 +18683,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 414, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -18634,7 +18748,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 415, + "weight": 416, "cookies": false, "type": "location", "deprecated": false, @@ -18725,7 +18839,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 417, + "weight": 418, "cookies": false, "type": "", "deprecated": false, @@ -18797,7 +18911,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 419, + "weight": 420, "cookies": false, "type": "", "deprecated": false, @@ -18869,7 +18983,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 418, + "weight": 419, "cookies": false, "type": "", "deprecated": false, @@ -18932,7 +19046,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 420, + "weight": 421, "cookies": false, "type": "", "deprecated": false, @@ -19004,7 +19118,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 423, + "weight": 424, "cookies": false, "type": "", "deprecated": false, @@ -19064,7 +19178,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 421, + "weight": 422, "cookies": false, "type": "", "deprecated": false, @@ -19156,7 +19270,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 422, + "weight": 423, "cookies": false, "type": "", "deprecated": false, @@ -19226,7 +19340,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 424, + "weight": 425, "cookies": false, "type": "", "deprecated": false, @@ -19318,7 +19432,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 425, + "weight": 426, "cookies": false, "type": "", "deprecated": false, @@ -19390,7 +19504,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 206, + "weight": 207, "cookies": false, "type": "", "deprecated": false, @@ -19464,7 +19578,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 205, + "weight": 206, "cookies": false, "type": "", "deprecated": false, @@ -19592,7 +19706,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 207, + "weight": 208, "cookies": false, "type": "", "deprecated": false, @@ -19652,7 +19766,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 208, + "weight": 209, "cookies": false, "type": "", "deprecated": false, @@ -19777,7 +19891,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 209, + "weight": 210, "cookies": false, "type": "", "deprecated": false, @@ -19839,7 +19953,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 211, + "weight": 212, "cookies": false, "type": "", "deprecated": false, @@ -19927,7 +20041,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 210, + "weight": 211, "cookies": false, "type": "upload", "deprecated": false, @@ -20027,7 +20141,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 212, + "weight": 213, "cookies": false, "type": "", "deprecated": false, @@ -20101,7 +20215,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 217, + "weight": 218, "cookies": false, "type": "", "deprecated": false, @@ -20192,7 +20306,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 218, + "weight": 219, "cookies": false, "type": "", "deprecated": false, @@ -20261,7 +20375,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 214, + "weight": 215, "cookies": false, "type": "location", "deprecated": false, @@ -20341,7 +20455,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 213, + "weight": 214, "cookies": false, "type": "location", "deprecated": false, @@ -20570,7 +20684,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 215, + "weight": 216, "cookies": false, "type": "location", "deprecated": false, @@ -20657,7 +20771,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 222, + "weight": 223, "cookies": false, "type": "", "deprecated": false, @@ -20735,7 +20849,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 221, + "weight": 222, "cookies": false, "type": "", "deprecated": false, @@ -20822,7 +20936,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 223, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -20886,7 +21000,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 225, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -20962,7 +21076,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 227, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -21028,7 +21142,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 229, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -21116,7 +21230,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 228, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -21229,7 +21343,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 230, + "weight": 231, "cookies": false, "type": "", "deprecated": false, @@ -21303,7 +21417,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 231, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -21392,7 +21506,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 233, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -21468,7 +21582,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 232, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -21567,7 +21681,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 224, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -21629,7 +21743,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 226, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -21712,7 +21826,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 438, + "weight": 439, "cookies": false, "type": "", "deprecated": false, @@ -21777,7 +21891,7 @@ "tags": [ "tokens" ], - "description": "Create a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "description": "Create a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "responses": { "201": { "description": "ResourceToken", @@ -21793,12 +21907,12 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 436, + "weight": 437, "cookies": false, "type": "", "deprecated": false, "demo": "tokens\/create-file-token.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "rate-limit": 60, "rate-time": 60, "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", @@ -21883,7 +21997,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 437, + "weight": 438, "cookies": false, "type": "", "deprecated": false, @@ -21944,7 +22058,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 439, + "weight": 440, "cookies": false, "type": "", "deprecated": false, @@ -22015,7 +22129,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 440, + "weight": 441, "cookies": false, "type": "", "deprecated": false, @@ -22078,7 +22192,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 244, + "weight": 245, "cookies": false, "type": "", "deprecated": false, @@ -22152,7 +22266,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 235, + "weight": 236, "cookies": false, "type": "", "deprecated": false, @@ -22241,7 +22355,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 238, + "weight": 239, "cookies": false, "type": "", "deprecated": false, @@ -22327,7 +22441,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 236, + "weight": 237, "cookies": false, "type": "", "deprecated": false, @@ -22413,7 +22527,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 252, + "weight": 253, "cookies": false, "type": "", "deprecated": false, @@ -22482,7 +22596,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 275, + "weight": 276, "cookies": false, "type": "", "deprecated": false, @@ -22544,7 +22658,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 237, + "weight": 238, "cookies": false, "type": "", "deprecated": false, @@ -22630,7 +22744,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 240, + "weight": 241, "cookies": false, "type": "", "deprecated": false, @@ -22716,7 +22830,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 241, + "weight": 242, "cookies": false, "type": "", "deprecated": false, @@ -22832,7 +22946,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 242, + "weight": 243, "cookies": false, "type": "", "deprecated": false, @@ -22936,7 +23050,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 239, + "weight": 240, "cookies": false, "type": "", "deprecated": false, @@ -23042,7 +23156,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 245, + "weight": 246, "cookies": false, "type": "", "deprecated": false, @@ -23095,7 +23209,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 273, + "weight": 274, "cookies": false, "type": "", "deprecated": false, @@ -23157,7 +23271,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 258, + "weight": 259, "cookies": false, "type": "", "deprecated": false, @@ -23238,7 +23352,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 276, + "weight": 277, "cookies": false, "type": "", "deprecated": false, @@ -23321,7 +23435,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 254, + "weight": 255, "cookies": false, "type": "", "deprecated": false, @@ -23405,7 +23519,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 250, + "weight": 251, "cookies": false, "type": "", "deprecated": false, @@ -23480,7 +23594,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 249, + "weight": 250, "cookies": false, "type": "", "deprecated": false, @@ -23566,7 +23680,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 263, + "weight": 264, "cookies": false, "type": "", "deprecated": false, @@ -23640,7 +23754,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 268, + "weight": 269, "cookies": false, "type": "", "deprecated": false, @@ -23717,7 +23831,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 264, + "weight": 265, "cookies": false, "type": "", "deprecated": false, @@ -23779,7 +23893,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 265, + "weight": 266, "cookies": false, "type": "", "deprecated": false, @@ -23839,7 +23953,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 267, + "weight": 268, "cookies": false, "type": "", "deprecated": false, @@ -23899,7 +24013,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 266, + "weight": 267, "cookies": false, "type": "", "deprecated": false, @@ -23961,7 +24075,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 256, + "weight": 257, "cookies": false, "type": "", "deprecated": false, @@ -24042,7 +24156,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 257, + "weight": 258, "cookies": false, "type": "", "deprecated": false, @@ -24123,7 +24237,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 259, + "weight": 260, "cookies": false, "type": "", "deprecated": false, @@ -24204,7 +24318,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 246, + "weight": 247, "cookies": false, "type": "", "deprecated": false, @@ -24264,7 +24378,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 261, + "weight": 262, "cookies": false, "type": "", "deprecated": false, @@ -24345,7 +24459,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 248, + "weight": 249, "cookies": false, "type": "", "deprecated": false, @@ -24405,7 +24519,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 269, + "weight": 270, "cookies": false, "type": "", "deprecated": false, @@ -24458,7 +24572,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 272, + "weight": 273, "cookies": false, "type": "", "deprecated": false, @@ -24513,7 +24627,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 271, + "weight": 272, "cookies": false, "type": "", "deprecated": false, @@ -24585,7 +24699,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 253, + "weight": 254, "cookies": false, "type": "", "deprecated": false, @@ -24666,7 +24780,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 251, + "weight": 252, "cookies": false, "type": "", "deprecated": false, @@ -24740,7 +24854,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 243, + "weight": 244, "cookies": false, "type": "", "deprecated": false, @@ -24851,7 +24965,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 247, + "weight": 248, "cookies": false, "type": "", "deprecated": false, @@ -24922,7 +25036,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 262, + "weight": 263, "cookies": false, "type": "", "deprecated": false, @@ -25012,7 +25126,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 274, + "weight": 275, "cookies": false, "type": "", "deprecated": false, @@ -25085,7 +25199,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 270, + "weight": 271, "cookies": false, "type": "", "deprecated": false, @@ -25168,7 +25282,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 260, + "weight": 261, "cookies": false, "type": "", "deprecated": false, @@ -25249,7 +25363,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 255, + "weight": 256, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 92e64c7ba0..8f03738786 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -1,7 +1,7 @@ { "openapi": "3.0.0", "info": { - "version": "1.7.0", + "version": "1.7.4", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -4457,6 +4457,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "client", "server", "server" @@ -4466,6 +4467,7 @@ { "name": "createDocument", "auth": { + "Admin": [], "Session": [], "Key": [], "JWT": [] @@ -4660,6 +4662,115 @@ } ] }, + "put": { + "summary": "Upsert document", + "operationId": "databasesUpsertDocument", + "tags": [ + "databases" + ], + "description": "Create or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "responses": { + "200": { + "description": "Document", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/document" + } + } + } + } + }, + "x-appwrite": { + "method": "upsertDocument", + "group": "documents", + "weight": 114, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/upsert-document.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/upsert-document.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "client", + "server", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Document data as JSON object. Include all required attributes of the document to be created or updated.", + "x-example": "{}" + }, + "permissions": { + "type": "array", + "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", + "x-example": "[\"read(\"any\")\"]", + "items": { + "type": "string" + } + } + }, + "required": [ + "data" + ] + } + } + } + } + }, "patch": { "summary": "Update document", "operationId": "databasesUpdateDocument", @@ -4781,7 +4892,7 @@ "x-appwrite": { "method": "deleteDocument", "group": "documents", - "weight": 116, + "weight": 119, "cookies": false, "type": "", "deprecated": false, @@ -4842,6 +4953,236 @@ ] } }, + "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/decrement": { + "patch": { + "summary": "Decrement document attribute", + "operationId": "databasesDecrementDocumentAttribute", + "tags": [ + "databases" + ], + "description": "Decrement a specific attribute of a document by a given value.", + "responses": { + "200": { + "description": "Document", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/document" + } + } + } + } + }, + "x-appwrite": { + "method": "decrementDocumentAttribute", + "group": "documents", + "weight": 116, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/decrement-document-attribute.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/decrement-document-attribute.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "console", + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "attribute", + "description": "Attribute key.", + "required": true, + "schema": { + "type": "string" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "value": { + "type": "number", + "description": "Value to decrement the attribute by. The value must be a number.", + "x-example": null + }, + "min": { + "type": "number", + "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", + "x-example": null + } + } + } + } + } + } + } + }, + "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/increment": { + "patch": { + "summary": "Increment document attribute", + "operationId": "databasesIncrementDocumentAttribute", + "tags": [ + "databases" + ], + "description": "Increment a specific attribute of a document by a given value.", + "responses": { + "200": { + "description": "Document", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/document" + } + } + } + } + }, + "x-appwrite": { + "method": "incrementDocumentAttribute", + "group": "documents", + "weight": 115, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/increment-document-attribute.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/increment-document-attribute.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "console", + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "attribute", + "description": "Attribute key.", + "required": true, + "schema": { + "type": "string" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "value": { + "type": "number", + "description": "Value to increment the attribute by. The value must be a number.", + "x-example": null + }, + "max": { + "type": "number", + "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", + "x-example": null + } + } + } + } + } + } + } + }, "\/functions\/{functionId}\/executions": { "get": { "summary": "List executions", @@ -4865,7 +5206,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 391, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -4940,7 +5281,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 389, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -5055,7 +5396,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 390, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -5129,7 +5470,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 305, + "weight": 308, "cookies": false, "type": "graphql", "deprecated": false, @@ -5181,7 +5522,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 304, + "weight": 307, "cookies": false, "type": "graphql", "deprecated": false, @@ -5233,7 +5574,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 121, + "weight": 124, "cookies": false, "type": "", "deprecated": false, @@ -5285,7 +5626,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 122, + "weight": 125, "cookies": false, "type": "", "deprecated": false, @@ -5337,7 +5678,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 126, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -5389,7 +5730,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 123, + "weight": 126, "cookies": false, "type": "", "deprecated": false, @@ -5441,7 +5782,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 124, + "weight": 127, "cookies": false, "type": "", "deprecated": false, @@ -5493,7 +5834,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 125, + "weight": 128, "cookies": false, "type": "", "deprecated": false, @@ -5545,7 +5886,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 127, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -5597,7 +5938,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 128, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -5649,7 +5990,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 351, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -5732,7 +6073,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 355, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -5807,7 +6148,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 211, + "weight": 214, "cookies": false, "type": "", "deprecated": false, @@ -5893,7 +6234,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 210, + "weight": 213, "cookies": false, "type": "upload", "deprecated": false, @@ -5991,7 +6332,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 212, + "weight": 215, "cookies": false, "type": "", "deprecated": false, @@ -6063,7 +6404,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 217, + "weight": 220, "cookies": false, "type": "", "deprecated": false, @@ -6152,7 +6493,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 218, + "weight": 221, "cookies": false, "type": "", "deprecated": false, @@ -6219,7 +6560,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 214, + "weight": 217, "cookies": false, "type": "location", "deprecated": false, @@ -6297,7 +6638,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 213, + "weight": 216, "cookies": false, "type": "location", "deprecated": false, @@ -6486,7 +6827,8 @@ "png", "webp", "heic", - "avif" + "avif", + "gif" ], "x-enum-name": "ImageFormat", "x-enum-keys": [], @@ -6524,7 +6866,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 215, + "weight": 218, "cookies": false, "type": "location", "deprecated": false, @@ -6609,7 +6951,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 222, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -6685,7 +7027,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 221, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -6770,7 +7112,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 223, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -6832,7 +7174,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 225, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -6906,7 +7248,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 227, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -6970,7 +7312,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 229, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -7056,7 +7398,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 228, + "weight": 231, "cookies": false, "type": "", "deprecated": false, @@ -7167,7 +7509,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 230, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -7239,7 +7581,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 231, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -7326,7 +7668,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 233, + "weight": 236, "cookies": false, "type": "", "deprecated": false, @@ -7400,7 +7742,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 232, + "weight": 235, "cookies": false, "type": "", "deprecated": false, @@ -7498,7 +7840,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 224, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -7559,7 +7901,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 226, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -8073,6 +8415,12 @@ "description": "Document ID.", "x-example": "5e5ea5c16897e" }, + "$sequence": { + "type": "integer", + "description": "Document automatically incrementing ID.", + "x-example": 1, + "format": "int32" + }, "$collectionId": { "type": "string", "description": "Collection ID.", @@ -8107,6 +8455,7 @@ "additionalProperties": true, "required": [ "$id", + "$sequence", "$collectionId", "$databaseId", "$createdAt", @@ -9752,6 +10101,12 @@ "name": "X-Appwrite-Session", "description": "The user session to authenticate with", "in": "header" + }, + "DevKey": { + "type": "apiKey", + "name": "X-Appwrite-Dev-Key", + "description": "Your secret dev API key", + "in": "header" } } }, diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 31d5016e85..85ef1334d4 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -1,7 +1,7 @@ { "openapi": "3.0.0", "info": { - "version": "1.7.0", + "version": "1.7.4", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -4359,7 +4359,7 @@ "x-appwrite": { "method": "chat", "group": "console", - "weight": 307, + "weight": 310, "cookies": false, "type": "", "deprecated": false, @@ -4419,7 +4419,7 @@ "x-appwrite": { "method": "getResource", "group": null, - "weight": 431, + "weight": 434, "cookies": false, "type": "", "deprecated": false, @@ -4494,7 +4494,7 @@ "x-appwrite": { "method": "variables", "group": "console", - "weight": 306, + "weight": 309, "cookies": false, "type": "", "deprecated": false, @@ -4694,7 +4694,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 118, + "weight": 121, "cookies": false, "type": "", "deprecated": false, @@ -8016,6 +8016,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "client", "server", "server" @@ -8025,6 +8026,7 @@ { "name": "createDocument", "auth": { + "Admin": [], "Session": [], "Key": [], "JWT": [] @@ -8053,6 +8055,7 @@ { "name": "createDocuments", "auth": { + "Admin": [], "Key": [] }, "parameters": [ @@ -8167,7 +8170,7 @@ "x-appwrite": { "method": "upsertDocuments", "group": "documents", - "weight": 115, + "weight": 118, "cookies": false, "type": "", "deprecated": false, @@ -8178,6 +8181,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "server" ], "packaging": false, @@ -8227,7 +8231,10 @@ "type": "object" } } - } + }, + "required": [ + "documents" + ] } } } @@ -8255,7 +8262,7 @@ "x-appwrite": { "method": "updateDocuments", "group": "documents", - "weight": 114, + "weight": 117, "cookies": false, "type": "", "deprecated": false, @@ -8266,6 +8273,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "server" ], "packaging": false, @@ -8348,7 +8356,7 @@ "x-appwrite": { "method": "deleteDocuments", "group": "documents", - "weight": 117, + "weight": 120, "cookies": false, "type": "", "deprecated": false, @@ -8359,6 +8367,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "server" ], "packaging": false, @@ -8511,6 +8520,115 @@ } ] }, + "put": { + "summary": "Upsert document", + "operationId": "databasesUpsertDocument", + "tags": [ + "databases" + ], + "description": "Create or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "responses": { + "200": { + "description": "Document", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/document" + } + } + } + } + }, + "x-appwrite": { + "method": "upsertDocument", + "group": "documents", + "weight": 114, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/upsert-document.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/upsert-document.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "client", + "server", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Document data as JSON object. Include all required attributes of the document to be created or updated.", + "x-example": "{}" + }, + "permissions": { + "type": "array", + "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", + "x-example": "[\"read(\"any\")\"]", + "items": { + "type": "string" + } + } + }, + "required": [ + "data" + ] + } + } + } + } + }, "patch": { "summary": "Update document", "operationId": "databasesUpdateDocument", @@ -8632,7 +8750,7 @@ "x-appwrite": { "method": "deleteDocument", "group": "documents", - "weight": 116, + "weight": 119, "cookies": false, "type": "", "deprecated": false, @@ -8786,6 +8904,236 @@ ] } }, + "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/decrement": { + "patch": { + "summary": "Decrement document attribute", + "operationId": "databasesDecrementDocumentAttribute", + "tags": [ + "databases" + ], + "description": "Decrement a specific attribute of a document by a given value.", + "responses": { + "200": { + "description": "Document", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/document" + } + } + } + } + }, + "x-appwrite": { + "method": "decrementDocumentAttribute", + "group": "documents", + "weight": 116, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/decrement-document-attribute.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/decrement-document-attribute.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "console", + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "attribute", + "description": "Attribute key.", + "required": true, + "schema": { + "type": "string" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "value": { + "type": "number", + "description": "Value to decrement the attribute by. The value must be a number.", + "x-example": null + }, + "min": { + "type": "number", + "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", + "x-example": null + } + } + } + } + } + } + } + }, + "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/increment": { + "patch": { + "summary": "Increment document attribute", + "operationId": "databasesIncrementDocumentAttribute", + "tags": [ + "databases" + ], + "description": "Increment a specific attribute of a document by a given value.", + "responses": { + "200": { + "description": "Document", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/document" + } + } + } + } + }, + "x-appwrite": { + "method": "incrementDocumentAttribute", + "group": "documents", + "weight": 115, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/increment-document-attribute.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/increment-document-attribute.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "console", + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "attribute", + "description": "Attribute key.", + "required": true, + "schema": { + "type": "string" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "value": { + "type": "number", + "description": "Value to increment the attribute by. The value must be a number.", + "x-example": null + }, + "max": { + "type": "number", + "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", + "x-example": null + } + } + } + } + } + } + } + }, "\/databases\/{databaseId}\/collections\/{collectionId}\/indexes": { "get": { "summary": "List indexes", @@ -9253,7 +9601,7 @@ "x-appwrite": { "method": "getCollectionUsage", "group": null, - "weight": 120, + "weight": 123, "cookies": false, "type": "", "deprecated": false, @@ -9418,7 +9766,7 @@ "x-appwrite": { "method": "getDatabaseUsage", "group": null, - "weight": 119, + "weight": 122, "cookies": false, "type": "", "deprecated": false, @@ -9500,7 +9848,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 375, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -9573,7 +9921,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 372, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -9655,6 +10003,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -9680,7 +10029,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -9804,7 +10154,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 377, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -9853,7 +10203,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 378, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -9903,7 +10253,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 401, + "weight": 404, "cookies": false, "type": "", "deprecated": false, @@ -10003,7 +10353,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 400, + "weight": 403, "cookies": false, "type": "", "deprecated": false, @@ -10063,7 +10413,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 394, + "weight": 397, "cookies": false, "type": "", "deprecated": false, @@ -10135,7 +10485,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 373, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -10194,7 +10544,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 374, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -10283,6 +10633,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -10308,7 +10659,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -10422,7 +10774,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 376, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -10483,7 +10835,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 381, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -10563,7 +10915,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 382, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -10646,7 +10998,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 379, + "weight": 382, "cookies": false, "type": "upload", "deprecated": false, @@ -10742,7 +11094,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 387, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -10827,7 +11179,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 384, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -10930,7 +11282,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 385, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -11027,7 +11379,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 380, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -11089,7 +11441,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 383, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -11153,7 +11505,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 386, + "weight": 389, "cookies": false, "type": "location", "deprecated": false, @@ -11243,7 +11595,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 388, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -11314,7 +11666,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 391, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -11389,7 +11741,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 389, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -11504,7 +11856,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 390, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -11569,7 +11921,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 392, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -11640,7 +11992,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 393, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -11722,7 +12074,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 397, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -11781,7 +12133,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 395, + "weight": 398, "cookies": false, "type": "", "deprecated": false, @@ -11872,7 +12224,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 396, + "weight": 399, "cookies": false, "type": "", "deprecated": false, @@ -11941,7 +12293,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 398, + "weight": 401, "cookies": false, "type": "", "deprecated": false, @@ -12032,7 +12384,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 399, + "weight": 402, "cookies": false, "type": "", "deprecated": false, @@ -12103,7 +12455,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 305, + "weight": 308, "cookies": false, "type": "graphql", "deprecated": false, @@ -12155,7 +12507,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 304, + "weight": 307, "cookies": false, "type": "graphql", "deprecated": false, @@ -12207,7 +12559,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 129, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -12256,7 +12608,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 150, + "weight": 153, "cookies": false, "type": "", "deprecated": false, @@ -12305,7 +12657,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 132, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -12354,7 +12706,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 137, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -12414,7 +12766,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 131, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -12463,7 +12815,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 133, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -12512,7 +12864,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 139, + "weight": 142, "cookies": false, "type": "", "deprecated": false, @@ -12574,7 +12926,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 138, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -12636,7 +12988,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 140, + "weight": 143, "cookies": false, "type": "", "deprecated": false, @@ -12709,7 +13061,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 141, + "weight": 144, "cookies": false, "type": "", "deprecated": false, @@ -12771,7 +13123,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 151, + "weight": 154, "cookies": false, "type": "", "deprecated": false, @@ -12859,7 +13211,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 145, + "weight": 148, "cookies": false, "type": "", "deprecated": false, @@ -12921,7 +13273,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 136, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -12983,7 +13335,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 142, + "weight": 145, "cookies": false, "type": "", "deprecated": false, @@ -13045,7 +13397,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 143, + "weight": 146, "cookies": false, "type": "", "deprecated": false, @@ -13107,7 +13459,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 144, + "weight": 147, "cookies": false, "type": "", "deprecated": false, @@ -13169,7 +13521,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 146, + "weight": 149, "cookies": false, "type": "", "deprecated": false, @@ -13231,7 +13583,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 147, + "weight": 150, "cookies": false, "type": "", "deprecated": false, @@ -13293,7 +13645,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 135, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -13355,7 +13707,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 149, + "weight": 152, "cookies": false, "type": "", "deprecated": false, @@ -13404,7 +13756,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 148, + "weight": 151, "cookies": false, "type": "", "deprecated": false, @@ -13453,7 +13805,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 134, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -13502,7 +13854,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 121, + "weight": 124, "cookies": false, "type": "", "deprecated": false, @@ -13554,7 +13906,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 122, + "weight": 125, "cookies": false, "type": "", "deprecated": false, @@ -13606,7 +13958,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 126, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -13658,7 +14010,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 123, + "weight": 126, "cookies": false, "type": "", "deprecated": false, @@ -13710,7 +14062,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 124, + "weight": 127, "cookies": false, "type": "", "deprecated": false, @@ -13762,7 +14114,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 125, + "weight": 128, "cookies": false, "type": "", "deprecated": false, @@ -13814,7 +14166,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 127, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -13866,7 +14218,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 128, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -13918,7 +14270,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 359, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -13994,7 +14346,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 356, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -14138,7 +14490,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 363, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -14284,7 +14636,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 358, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -14458,7 +14810,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 365, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -14636,7 +14988,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 357, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -14745,7 +15097,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 364, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -14857,7 +15209,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 362, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -14910,7 +15262,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 366, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -14972,7 +15324,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 360, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -15047,7 +15399,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 361, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -15122,7 +15474,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 331, + "weight": 334, "cookies": false, "type": "", "deprecated": false, @@ -15198,7 +15550,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 330, + "weight": 333, "cookies": false, "type": "", "deprecated": false, @@ -15303,7 +15655,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 343, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -15411,7 +15763,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 329, + "weight": 332, "cookies": false, "type": "", "deprecated": false, @@ -15496,7 +15848,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 342, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -15584,7 +15936,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 321, + "weight": 324, "cookies": false, "type": "", "deprecated": false, @@ -15699,7 +16051,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 334, + "weight": 337, "cookies": false, "type": "", "deprecated": false, @@ -15817,7 +16169,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 324, + "weight": 327, "cookies": false, "type": "", "deprecated": false, @@ -15912,7 +16264,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 337, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -16010,7 +16362,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 322, + "weight": 325, "cookies": false, "type": "", "deprecated": false, @@ -16115,7 +16467,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 335, + "weight": 338, "cookies": false, "type": "", "deprecated": false, @@ -16223,7 +16575,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 323, + "weight": 326, "cookies": false, "type": "", "deprecated": false, @@ -16366,7 +16718,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 336, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -16511,7 +16863,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 325, + "weight": 328, "cookies": false, "type": "", "deprecated": false, @@ -16606,7 +16958,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 338, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -16704,7 +17056,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 326, + "weight": 329, "cookies": false, "type": "", "deprecated": false, @@ -16799,7 +17151,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 339, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -16897,7 +17249,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 327, + "weight": 330, "cookies": false, "type": "", "deprecated": false, @@ -16992,7 +17344,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 340, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -17090,7 +17442,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 328, + "weight": 331, "cookies": false, "type": "", "deprecated": false, @@ -17185,7 +17537,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 341, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -17283,7 +17635,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 333, + "weight": 336, "cookies": false, "type": "", "deprecated": false, @@ -17336,7 +17688,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 344, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -17398,7 +17750,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 332, + "weight": 335, "cookies": false, "type": "", "deprecated": false, @@ -17473,7 +17825,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 353, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -17548,7 +17900,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 346, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -17622,7 +17974,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 345, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -17705,7 +18057,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 348, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -17765,7 +18117,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 349, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -17842,7 +18194,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 350, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -17904,7 +18256,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 347, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -17979,7 +18331,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 352, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -18063,7 +18415,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 351, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -18153,7 +18505,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 354, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -18216,7 +18568,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 355, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -18291,7 +18643,7 @@ "x-appwrite": { "method": "list", "group": null, - "weight": 313, + "weight": 316, "cookies": false, "type": "", "deprecated": false, @@ -18365,7 +18717,7 @@ "x-appwrite": { "method": "createAppwriteMigration", "group": null, - "weight": 308, + "weight": 311, "cookies": false, "type": "", "deprecated": false, @@ -18453,7 +18805,7 @@ "x-appwrite": { "method": "getAppwriteReport", "group": null, - "weight": 315, + "weight": 318, "cookies": false, "type": "", "deprecated": false, @@ -18546,7 +18898,7 @@ "x-appwrite": { "method": "createCsvMigration", "group": null, - "weight": 312, + "weight": 315, "cookies": false, "type": "", "deprecated": false, @@ -18625,7 +18977,7 @@ "x-appwrite": { "method": "createFirebaseMigration", "group": null, - "weight": 309, + "weight": 312, "cookies": false, "type": "", "deprecated": false, @@ -18701,7 +19053,7 @@ "x-appwrite": { "method": "getFirebaseReport", "group": null, - "weight": 316, + "weight": 319, "cookies": false, "type": "", "deprecated": false, @@ -18773,7 +19125,7 @@ "x-appwrite": { "method": "createNHostMigration", "group": null, - "weight": 311, + "weight": 314, "cookies": false, "type": "", "deprecated": false, @@ -18884,7 +19236,7 @@ "x-appwrite": { "method": "getNHostReport", "group": null, - "weight": 318, + "weight": 321, "cookies": false, "type": "", "deprecated": false, @@ -19017,7 +19369,7 @@ "x-appwrite": { "method": "createSupabaseMigration", "group": null, - "weight": 310, + "weight": 313, "cookies": false, "type": "", "deprecated": false, @@ -19122,7 +19474,7 @@ "x-appwrite": { "method": "getSupabaseReport", "group": null, - "weight": 317, + "weight": 320, "cookies": false, "type": "", "deprecated": false, @@ -19246,7 +19598,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 314, + "weight": 317, "cookies": false, "type": "", "deprecated": false, @@ -19304,7 +19656,7 @@ "x-appwrite": { "method": "retry", "group": null, - "weight": 319, + "weight": 322, "cookies": false, "type": "", "deprecated": false, @@ -19355,7 +19707,7 @@ "x-appwrite": { "method": "delete", "group": null, - "weight": 320, + "weight": 323, "cookies": false, "type": "", "deprecated": false, @@ -19415,7 +19767,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 199, + "weight": 202, "cookies": false, "type": "", "deprecated": false, @@ -19503,7 +19855,7 @@ "x-appwrite": { "method": "listVariables", "group": null, - "weight": 201, + "weight": 204, "cookies": false, "type": "", "deprecated": false, @@ -19549,7 +19901,7 @@ "x-appwrite": { "method": "createVariable", "group": null, - "weight": 200, + "weight": 203, "cookies": false, "type": "", "deprecated": false, @@ -19627,7 +19979,7 @@ "x-appwrite": { "method": "getVariable", "group": null, - "weight": 202, + "weight": 205, "cookies": false, "type": "", "deprecated": false, @@ -19685,7 +20037,7 @@ "x-appwrite": { "method": "updateVariable", "group": null, - "weight": 203, + "weight": 206, "cookies": false, "type": "", "deprecated": false, @@ -19765,7 +20117,7 @@ "x-appwrite": { "method": "deleteVariable", "group": null, - "weight": 204, + "weight": 207, "cookies": false, "type": "", "deprecated": false, @@ -19825,7 +20177,7 @@ "x-appwrite": { "method": "list", "group": "projects", - "weight": 154, + "weight": 157, "cookies": false, "type": "", "deprecated": false, @@ -19897,7 +20249,7 @@ "x-appwrite": { "method": "create", "group": "projects", - "weight": 153, + "weight": 156, "cookies": false, "type": "", "deprecated": false, @@ -20031,7 +20383,7 @@ "x-appwrite": { "method": "get", "group": "projects", - "weight": 155, + "weight": 158, "cookies": false, "type": "", "deprecated": false, @@ -20089,7 +20441,7 @@ "x-appwrite": { "method": "update", "group": "projects", - "weight": 156, + "weight": 159, "cookies": false, "type": "", "deprecated": false, @@ -20204,7 +20556,7 @@ "x-appwrite": { "method": "delete", "group": "projects", - "weight": 173, + "weight": 176, "cookies": false, "type": "", "deprecated": false, @@ -20264,7 +20616,7 @@ "x-appwrite": { "method": "updateApiStatus", "group": "projects", - "weight": 160, + "weight": 163, "cookies": false, "type": "", "deprecated": false, @@ -20356,7 +20708,7 @@ "x-appwrite": { "method": "updateApiStatusAll", "group": "projects", - "weight": 161, + "weight": 164, "cookies": false, "type": "", "deprecated": false, @@ -20435,7 +20787,7 @@ "x-appwrite": { "method": "updateAuthDuration", "group": "auth", - "weight": 166, + "weight": 169, "cookies": false, "type": "", "deprecated": false, @@ -20514,7 +20866,7 @@ "x-appwrite": { "method": "updateAuthLimit", "group": "auth", - "weight": 165, + "weight": 168, "cookies": false, "type": "", "deprecated": false, @@ -20593,7 +20945,7 @@ "x-appwrite": { "method": "updateAuthSessionsLimit", "group": "auth", - "weight": 171, + "weight": 174, "cookies": false, "type": "", "deprecated": false, @@ -20672,7 +21024,7 @@ "x-appwrite": { "method": "updateMembershipsPrivacy", "group": "auth", - "weight": 164, + "weight": 167, "cookies": false, "type": "", "deprecated": false, @@ -20763,7 +21115,7 @@ "x-appwrite": { "method": "updateMockNumbers", "group": "auth", - "weight": 172, + "weight": 175, "cookies": false, "type": "", "deprecated": false, @@ -20845,7 +21197,7 @@ "x-appwrite": { "method": "updateAuthPasswordDictionary", "group": "auth", - "weight": 169, + "weight": 172, "cookies": false, "type": "", "deprecated": false, @@ -20924,7 +21276,7 @@ "x-appwrite": { "method": "updateAuthPasswordHistory", "group": "auth", - "weight": 168, + "weight": 171, "cookies": false, "type": "", "deprecated": false, @@ -21003,7 +21355,7 @@ "x-appwrite": { "method": "updatePersonalDataCheck", "group": "auth", - "weight": 170, + "weight": 173, "cookies": false, "type": "", "deprecated": false, @@ -21082,7 +21434,7 @@ "x-appwrite": { "method": "updateSessionAlerts", "group": "auth", - "weight": 163, + "weight": 166, "cookies": false, "type": "", "deprecated": false, @@ -21161,7 +21513,7 @@ "x-appwrite": { "method": "updateAuthStatus", "group": "auth", - "weight": 167, + "weight": 170, "cookies": false, "type": "", "deprecated": false, @@ -21261,7 +21613,7 @@ "x-appwrite": { "method": "listDevKeys", "group": "devKeys", - "weight": 370, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -21329,7 +21681,7 @@ "x-appwrite": { "method": "createDevKey", "group": "devKeys", - "weight": 367, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -21414,7 +21766,7 @@ "x-appwrite": { "method": "getDevKey", "group": "devKeys", - "weight": 369, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -21482,7 +21834,7 @@ "x-appwrite": { "method": "updateDevKey", "group": "devKeys", - "weight": 368, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -21568,7 +21920,7 @@ "x-appwrite": { "method": "deleteDevKey", "group": "devKeys", - "weight": 371, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -21638,7 +21990,7 @@ "x-appwrite": { "method": "createJWT", "group": "auth", - "weight": 185, + "weight": 188, "cookies": false, "type": "", "deprecated": false, @@ -21725,7 +22077,7 @@ "x-appwrite": { "method": "listKeys", "group": "keys", - "weight": 181, + "weight": 184, "cookies": false, "type": "", "deprecated": false, @@ -21783,7 +22135,7 @@ "x-appwrite": { "method": "createKey", "group": "keys", - "weight": 180, + "weight": 183, "cookies": false, "type": "", "deprecated": false, @@ -21876,7 +22228,7 @@ "x-appwrite": { "method": "getKey", "group": "keys", - "weight": 182, + "weight": 185, "cookies": false, "type": "", "deprecated": false, @@ -21944,7 +22296,7 @@ "x-appwrite": { "method": "updateKey", "group": "keys", - "weight": 183, + "weight": 186, "cookies": false, "type": "", "deprecated": false, @@ -22038,7 +22390,7 @@ "x-appwrite": { "method": "deleteKey", "group": "keys", - "weight": 184, + "weight": 187, "cookies": false, "type": "", "deprecated": false, @@ -22108,7 +22460,7 @@ "x-appwrite": { "method": "updateOAuth2", "group": "auth", - "weight": 162, + "weight": 165, "cookies": false, "type": "", "deprecated": false, @@ -22246,7 +22598,7 @@ "x-appwrite": { "method": "listPlatforms", "group": "platforms", - "weight": 187, + "weight": 190, "cookies": false, "type": "", "deprecated": false, @@ -22304,7 +22656,7 @@ "x-appwrite": { "method": "createPlatform", "group": "platforms", - "weight": 186, + "weight": 189, "cookies": false, "type": "", "deprecated": false, @@ -22423,7 +22775,7 @@ "x-appwrite": { "method": "getPlatform", "group": "platforms", - "weight": 188, + "weight": 191, "cookies": false, "type": "", "deprecated": false, @@ -22491,7 +22843,7 @@ "x-appwrite": { "method": "updatePlatform", "group": "platforms", - "weight": 189, + "weight": 192, "cookies": false, "type": "", "deprecated": false, @@ -22586,7 +22938,7 @@ "x-appwrite": { "method": "deletePlatform", "group": "platforms", - "weight": 190, + "weight": 193, "cookies": false, "type": "", "deprecated": false, @@ -22656,7 +23008,7 @@ "x-appwrite": { "method": "updateServiceStatus", "group": "projects", - "weight": 158, + "weight": 161, "cookies": false, "type": "", "deprecated": false, @@ -22757,7 +23109,7 @@ "x-appwrite": { "method": "updateServiceStatusAll", "group": "projects", - "weight": 159, + "weight": 162, "cookies": false, "type": "", "deprecated": false, @@ -22836,7 +23188,7 @@ "x-appwrite": { "method": "updateSmtp", "group": "templates", - "weight": 191, + "weight": 194, "cookies": false, "type": "", "deprecated": false, @@ -22954,7 +23306,7 @@ "x-appwrite": { "method": "createSmtpTest", "group": "templates", - "weight": 192, + "weight": 195, "cookies": false, "type": "", "deprecated": false, @@ -23085,7 +23437,7 @@ "x-appwrite": { "method": "updateTeam", "group": "projects", - "weight": 157, + "weight": 160, "cookies": false, "type": "", "deprecated": false, @@ -23164,7 +23516,7 @@ "x-appwrite": { "method": "getEmailTemplate", "group": "templates", - "weight": 194, + "weight": 197, "cookies": false, "type": "", "deprecated": false, @@ -23388,7 +23740,7 @@ "x-appwrite": { "method": "updateEmailTemplate", "group": "templates", - "weight": 196, + "weight": 199, "cookies": false, "type": "", "deprecated": false, @@ -23652,7 +24004,7 @@ "x-appwrite": { "method": "deleteEmailTemplate", "group": "templates", - "weight": 198, + "weight": 201, "cookies": false, "type": "", "deprecated": false, @@ -23878,7 +24230,7 @@ "x-appwrite": { "method": "getSmsTemplate", "group": "templates", - "weight": 193, + "weight": 196, "cookies": false, "type": "", "deprecated": false, @@ -24099,7 +24451,7 @@ "x-appwrite": { "method": "updateSmsTemplate", "group": "templates", - "weight": 195, + "weight": 198, "cookies": false, "type": "", "deprecated": false, @@ -24339,7 +24691,7 @@ "x-appwrite": { "method": "deleteSmsTemplate", "group": "templates", - "weight": 197, + "weight": 200, "cookies": false, "type": "", "deprecated": false, @@ -24562,7 +24914,7 @@ "x-appwrite": { "method": "listWebhooks", "group": "webhooks", - "weight": 175, + "weight": 178, "cookies": false, "type": "", "deprecated": false, @@ -24620,7 +24972,7 @@ "x-appwrite": { "method": "createWebhook", "group": "webhooks", - "weight": 174, + "weight": 177, "cookies": false, "type": "", "deprecated": false, @@ -24735,7 +25087,7 @@ "x-appwrite": { "method": "getWebhook", "group": "webhooks", - "weight": 176, + "weight": 179, "cookies": false, "type": "", "deprecated": false, @@ -24803,7 +25155,7 @@ "x-appwrite": { "method": "updateWebhook", "group": "webhooks", - "weight": 177, + "weight": 180, "cookies": false, "type": "", "deprecated": false, @@ -24919,7 +25271,7 @@ "x-appwrite": { "method": "deleteWebhook", "group": "webhooks", - "weight": 179, + "weight": 182, "cookies": false, "type": "", "deprecated": false, @@ -24989,7 +25341,7 @@ "x-appwrite": { "method": "updateWebhookSignature", "group": "webhooks", - "weight": 178, + "weight": 181, "cookies": false, "type": "", "deprecated": false, @@ -25059,7 +25411,7 @@ "x-appwrite": { "method": "listRules", "group": null, - "weight": 291, + "weight": 294, "cookies": false, "type": "", "deprecated": false, @@ -25133,7 +25485,7 @@ "x-appwrite": { "method": "createAPIRule", "group": null, - "weight": 432, + "weight": 435, "cookies": false, "type": "", "deprecated": false, @@ -25200,7 +25552,7 @@ "x-appwrite": { "method": "createFunctionRule", "group": null, - "weight": 434, + "weight": 437, "cookies": false, "type": "", "deprecated": false, @@ -25278,7 +25630,7 @@ "x-appwrite": { "method": "createRedirectRule", "group": null, - "weight": 435, + "weight": 438, "cookies": false, "type": "", "deprecated": false, @@ -25334,12 +25686,33 @@ "Temporary Redirect 307", "Permanent Redirect 308" ] + }, + "resourceId": { + "type": "string", + "description": "ID of parent resource.", + "x-example": "" + }, + "resourceType": { + "type": "string", + "description": "Type of parent resource.", + "x-example": "site", + "enum": [ + "site", + "function" + ], + "x-enum-name": "ProxyResourceType", + "x-enum-keys": [ + "Site", + "Function" + ] } }, "required": [ "domain", "url", - "statusCode" + "statusCode", + "resourceId", + "resourceType" ] } } @@ -25370,7 +25743,7 @@ "x-appwrite": { "method": "createSiteRule", "group": null, - "weight": 433, + "weight": 436, "cookies": false, "type": "", "deprecated": false, @@ -25448,7 +25821,7 @@ "x-appwrite": { "method": "getRule", "group": null, - "weight": 292, + "weight": 295, "cookies": false, "type": "", "deprecated": false, @@ -25499,7 +25872,7 @@ "x-appwrite": { "method": "deleteRule", "group": null, - "weight": 293, + "weight": 296, "cookies": false, "type": "", "deprecated": false, @@ -25559,7 +25932,7 @@ "x-appwrite": { "method": "updateRuleVerification", "group": null, - "weight": 294, + "weight": 297, "cookies": false, "type": "", "deprecated": false, @@ -25619,7 +25992,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 404, + "weight": 407, "cookies": false, "type": "", "deprecated": false, @@ -25689,7 +26062,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 402, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -25824,6 +26197,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -25849,7 +26223,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -25936,7 +26311,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 407, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -25985,7 +26360,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 430, + "weight": 433, "cookies": false, "type": "", "deprecated": false, @@ -26035,7 +26410,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 426, + "weight": 429, "cookies": false, "type": "", "deprecated": false, @@ -26135,7 +26510,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 427, + "weight": 430, "cookies": false, "type": "", "deprecated": false, @@ -26195,7 +26570,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 428, + "weight": 431, "cookies": false, "type": "", "deprecated": false, @@ -26267,7 +26642,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 403, + "weight": 406, "cookies": false, "type": "", "deprecated": false, @@ -26326,7 +26701,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 405, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -26468,6 +26843,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -26493,7 +26869,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -26569,7 +26946,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 406, + "weight": 409, "cookies": false, "type": "", "deprecated": false, @@ -26630,7 +27007,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 413, + "weight": 416, "cookies": false, "type": "", "deprecated": false, @@ -26710,7 +27087,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 412, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -26793,7 +27170,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 408, + "weight": 411, "cookies": false, "type": "upload", "deprecated": false, @@ -26894,7 +27271,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 416, + "weight": 419, "cookies": false, "type": "", "deprecated": false, @@ -26974,7 +27351,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 409, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -27077,7 +27454,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 410, + "weight": 413, "cookies": false, "type": "", "deprecated": false, @@ -27175,7 +27552,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 411, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -27237,7 +27614,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 414, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -27301,7 +27678,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 415, + "weight": 418, "cookies": false, "type": "location", "deprecated": false, @@ -27391,7 +27768,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 417, + "weight": 420, "cookies": false, "type": "", "deprecated": false, @@ -27462,7 +27839,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 419, + "weight": 422, "cookies": false, "type": "", "deprecated": false, @@ -27533,7 +27910,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 418, + "weight": 421, "cookies": false, "type": "", "deprecated": false, @@ -27595,7 +27972,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 420, + "weight": 423, "cookies": false, "type": "", "deprecated": false, @@ -27666,7 +28043,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 429, + "weight": 432, "cookies": false, "type": "", "deprecated": false, @@ -27748,7 +28125,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 423, + "weight": 426, "cookies": false, "type": "", "deprecated": false, @@ -27807,7 +28184,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 421, + "weight": 424, "cookies": false, "type": "", "deprecated": false, @@ -27898,7 +28275,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 422, + "weight": 425, "cookies": false, "type": "", "deprecated": false, @@ -27967,7 +28344,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 424, + "weight": 427, "cookies": false, "type": "", "deprecated": false, @@ -28058,7 +28435,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 425, + "weight": 428, "cookies": false, "type": "", "deprecated": false, @@ -28129,7 +28506,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 206, + "weight": 209, "cookies": false, "type": "", "deprecated": false, @@ -28202,7 +28579,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 205, + "weight": 208, "cookies": false, "type": "", "deprecated": false, @@ -28329,7 +28706,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 207, + "weight": 210, "cookies": false, "type": "", "deprecated": false, @@ -28388,7 +28765,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 208, + "weight": 211, "cookies": false, "type": "", "deprecated": false, @@ -28512,7 +28889,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 209, + "weight": 212, "cookies": false, "type": "", "deprecated": false, @@ -28573,7 +28950,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 211, + "weight": 214, "cookies": false, "type": "", "deprecated": false, @@ -28659,7 +29036,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 210, + "weight": 213, "cookies": false, "type": "upload", "deprecated": false, @@ -28757,7 +29134,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 212, + "weight": 215, "cookies": false, "type": "", "deprecated": false, @@ -28829,7 +29206,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 217, + "weight": 220, "cookies": false, "type": "", "deprecated": false, @@ -28918,7 +29295,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 218, + "weight": 221, "cookies": false, "type": "", "deprecated": false, @@ -28985,7 +29362,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 214, + "weight": 217, "cookies": false, "type": "location", "deprecated": false, @@ -29063,7 +29440,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 213, + "weight": 216, "cookies": false, "type": "location", "deprecated": false, @@ -29252,7 +29629,8 @@ "png", "webp", "heic", - "avif" + "avif", + "gif" ], "x-enum-name": "ImageFormat", "x-enum-keys": [], @@ -29290,7 +29668,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 215, + "weight": 218, "cookies": false, "type": "location", "deprecated": false, @@ -29375,7 +29753,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 219, + "weight": 222, "cookies": false, "type": "", "deprecated": false, @@ -29447,7 +29825,7 @@ "x-appwrite": { "method": "getBucketUsage", "group": null, - "weight": 220, + "weight": 223, "cookies": false, "type": "", "deprecated": false, @@ -29529,7 +29907,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 222, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -29605,7 +29983,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 221, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -29690,7 +30068,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 223, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -29752,7 +30130,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 225, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -29826,7 +30204,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 227, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -29890,7 +30268,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 234, + "weight": 237, "cookies": false, "type": "", "deprecated": false, @@ -29963,7 +30341,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 229, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -30049,7 +30427,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 228, + "weight": 231, "cookies": false, "type": "", "deprecated": false, @@ -30160,7 +30538,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 230, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -30232,7 +30610,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 231, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -30319,7 +30697,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 233, + "weight": 236, "cookies": false, "type": "", "deprecated": false, @@ -30393,7 +30771,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 232, + "weight": 235, "cookies": false, "type": "", "deprecated": false, @@ -30490,7 +30868,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 224, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -30550,7 +30928,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 226, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -30631,7 +31009,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 438, + "weight": 441, "cookies": false, "type": "", "deprecated": false, @@ -30695,7 +31073,7 @@ "tags": [ "tokens" ], - "description": "Create a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "description": "Create a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "responses": { "201": { "description": "ResourceToken", @@ -30711,12 +31089,12 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 436, + "weight": 439, "cookies": false, "type": "", "deprecated": false, "demo": "tokens\/create-file-token.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "rate-limit": 60, "rate-time": 60, "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", @@ -30800,7 +31178,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 437, + "weight": 440, "cookies": false, "type": "", "deprecated": false, @@ -30860,7 +31238,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 439, + "weight": 442, "cookies": false, "type": "", "deprecated": false, @@ -30930,7 +31308,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 440, + "weight": 443, "cookies": false, "type": "", "deprecated": false, @@ -30992,7 +31370,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 244, + "weight": 247, "cookies": false, "type": "", "deprecated": false, @@ -31065,7 +31443,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 235, + "weight": 238, "cookies": false, "type": "", "deprecated": false, @@ -31153,7 +31531,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 238, + "weight": 241, "cookies": false, "type": "", "deprecated": false, @@ -31238,7 +31616,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 236, + "weight": 239, "cookies": false, "type": "", "deprecated": false, @@ -31323,7 +31701,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 252, + "weight": 255, "cookies": false, "type": "", "deprecated": false, @@ -31391,7 +31769,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 275, + "weight": 278, "cookies": false, "type": "", "deprecated": false, @@ -31452,7 +31830,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 237, + "weight": 240, "cookies": false, "type": "", "deprecated": false, @@ -31537,7 +31915,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 240, + "weight": 243, "cookies": false, "type": "", "deprecated": false, @@ -31622,7 +32000,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 241, + "weight": 244, "cookies": false, "type": "", "deprecated": false, @@ -31737,7 +32115,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 242, + "weight": 245, "cookies": false, "type": "", "deprecated": false, @@ -31840,7 +32218,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 239, + "weight": 242, "cookies": false, "type": "", "deprecated": false, @@ -31945,7 +32323,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 277, + "weight": 280, "cookies": false, "type": "", "deprecated": false, @@ -32017,7 +32395,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 245, + "weight": 248, "cookies": false, "type": "", "deprecated": false, @@ -32069,7 +32447,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 273, + "weight": 276, "cookies": false, "type": "", "deprecated": false, @@ -32130,7 +32508,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 258, + "weight": 261, "cookies": false, "type": "", "deprecated": false, @@ -32210,7 +32588,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 276, + "weight": 279, "cookies": false, "type": "", "deprecated": false, @@ -32292,7 +32670,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 254, + "weight": 257, "cookies": false, "type": "", "deprecated": false, @@ -32375,7 +32753,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 250, + "weight": 253, "cookies": false, "type": "", "deprecated": false, @@ -32449,7 +32827,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 249, + "weight": 252, "cookies": false, "type": "", "deprecated": false, @@ -32534,7 +32912,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 263, + "weight": 266, "cookies": false, "type": "", "deprecated": false, @@ -32607,7 +32985,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 268, + "weight": 271, "cookies": false, "type": "", "deprecated": false, @@ -32683,7 +33061,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 264, + "weight": 267, "cookies": false, "type": "", "deprecated": false, @@ -32744,7 +33122,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 265, + "weight": 268, "cookies": false, "type": "", "deprecated": false, @@ -32803,7 +33181,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 267, + "weight": 270, "cookies": false, "type": "", "deprecated": false, @@ -32862,7 +33240,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 266, + "weight": 269, "cookies": false, "type": "", "deprecated": false, @@ -32923,7 +33301,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 256, + "weight": 259, "cookies": false, "type": "", "deprecated": false, @@ -33003,7 +33381,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 257, + "weight": 260, "cookies": false, "type": "", "deprecated": false, @@ -33083,7 +33461,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 259, + "weight": 262, "cookies": false, "type": "", "deprecated": false, @@ -33163,7 +33541,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 246, + "weight": 249, "cookies": false, "type": "", "deprecated": false, @@ -33222,7 +33600,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 261, + "weight": 264, "cookies": false, "type": "", "deprecated": false, @@ -33302,7 +33680,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 248, + "weight": 251, "cookies": false, "type": "", "deprecated": false, @@ -33361,7 +33739,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 269, + "weight": 272, "cookies": false, "type": "", "deprecated": false, @@ -33413,7 +33791,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 272, + "weight": 275, "cookies": false, "type": "", "deprecated": false, @@ -33467,7 +33845,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 271, + "weight": 274, "cookies": false, "type": "", "deprecated": false, @@ -33538,7 +33916,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 253, + "weight": 256, "cookies": false, "type": "", "deprecated": false, @@ -33618,7 +33996,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 251, + "weight": 254, "cookies": false, "type": "", "deprecated": false, @@ -33656,7 +34034,7 @@ }, { "name": "queries", - "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: name, email, phone, status, passwordUpdate, registration, emailVerification, phoneVerification, labels", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, providerId, identifier, providerType", "required": false, "schema": { "type": "array", @@ -33691,7 +34069,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 243, + "weight": 246, "cookies": false, "type": "", "deprecated": false, @@ -33801,7 +34179,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 247, + "weight": 250, "cookies": false, "type": "", "deprecated": false, @@ -33871,7 +34249,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 262, + "weight": 265, "cookies": false, "type": "", "deprecated": false, @@ -33960,7 +34338,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 274, + "weight": 277, "cookies": false, "type": "", "deprecated": false, @@ -34032,7 +34410,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 270, + "weight": 273, "cookies": false, "type": "", "deprecated": false, @@ -34114,7 +34492,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 260, + "weight": 263, "cookies": false, "type": "", "deprecated": false, @@ -34194,7 +34572,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 255, + "weight": 258, "cookies": false, "type": "", "deprecated": false, @@ -34274,7 +34652,7 @@ "x-appwrite": { "method": "createRepositoryDetection", "group": "repositories", - "weight": 281, + "weight": 284, "cookies": false, "type": "", "deprecated": false, @@ -34370,7 +34748,7 @@ "x-appwrite": { "method": "listRepositories", "group": "repositories", - "weight": 282, + "weight": 285, "cookies": false, "type": "", "deprecated": false, @@ -34455,7 +34833,7 @@ "x-appwrite": { "method": "createRepository", "group": "repositories", - "weight": 283, + "weight": 286, "cookies": false, "type": "", "deprecated": false, @@ -34540,7 +34918,7 @@ "x-appwrite": { "method": "getRepository", "group": "repositories", - "weight": 284, + "weight": 287, "cookies": false, "type": "", "deprecated": false, @@ -34610,7 +34988,7 @@ "x-appwrite": { "method": "listRepositoryBranches", "group": "repositories", - "weight": 285, + "weight": 288, "cookies": false, "type": "", "deprecated": false, @@ -34680,7 +35058,7 @@ "x-appwrite": { "method": "getRepositoryContents", "group": "repositories", - "weight": 280, + "weight": 283, "cookies": false, "type": "", "deprecated": false, @@ -34754,7 +35132,7 @@ "x-appwrite": { "method": "updateExternalDeployments", "group": "repositories", - "weight": 290, + "weight": 293, "cookies": false, "type": "", "deprecated": false, @@ -34843,7 +35221,7 @@ "x-appwrite": { "method": "listInstallations", "group": "installations", - "weight": 287, + "weight": 290, "cookies": false, "type": "", "deprecated": false, @@ -34917,7 +35295,7 @@ "x-appwrite": { "method": "getInstallation", "group": "installations", - "weight": 288, + "weight": 291, "cookies": false, "type": "", "deprecated": false, @@ -34968,7 +35346,7 @@ "x-appwrite": { "method": "deleteInstallation", "group": "installations", - "weight": 289, + "weight": 292, "cookies": false, "type": "", "deprecated": false, @@ -36478,6 +36856,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -37229,6 +37613,12 @@ "description": "Document ID.", "x-example": "5e5ea5c16897e" }, + "$sequence": { + "type": "integer", + "description": "Document automatically incrementing ID.", + "x-example": 1, + "format": "int32" + }, "$collectionId": { "type": "string", "description": "Collection ID.", @@ -37263,6 +37653,7 @@ "additionalProperties": true, "required": [ "$id", + "$sequence", "$collectionId", "$databaseId", "$createdAt", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index ac1c26d5b6..3e9b87fdf1 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -1,7 +1,7 @@ { "openapi": "3.0.0", "info": { - "version": "1.7.0", + "version": "1.7.4", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -7497,6 +7497,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "client", "server", "server" @@ -7506,6 +7507,7 @@ { "name": "createDocument", "auth": { + "Admin": [], "Session": [], "Key": [], "JWT": [] @@ -7534,6 +7536,7 @@ { "name": "createDocuments", "auth": { + "Admin": [], "Key": [] }, "parameters": [ @@ -7650,7 +7653,7 @@ "x-appwrite": { "method": "upsertDocuments", "group": "documents", - "weight": 115, + "weight": 118, "cookies": false, "type": "", "deprecated": false, @@ -7661,6 +7664,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "server" ], "packaging": false, @@ -7711,7 +7715,10 @@ "type": "object" } } - } + }, + "required": [ + "documents" + ] } } } @@ -7739,7 +7746,7 @@ "x-appwrite": { "method": "updateDocuments", "group": "documents", - "weight": 114, + "weight": 117, "cookies": false, "type": "", "deprecated": false, @@ -7750,6 +7757,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "server" ], "packaging": false, @@ -7833,7 +7841,7 @@ "x-appwrite": { "method": "deleteDocuments", "group": "documents", - "weight": 117, + "weight": 120, "cookies": false, "type": "", "deprecated": false, @@ -7844,6 +7852,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "server" ], "packaging": false, @@ -7999,6 +8008,117 @@ } ] }, + "put": { + "summary": "Upsert document", + "operationId": "databasesUpsertDocument", + "tags": [ + "databases" + ], + "description": "Create or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "responses": { + "200": { + "description": "Document", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/document" + } + } + } + } + }, + "x-appwrite": { + "method": "upsertDocument", + "group": "documents", + "weight": 114, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/upsert-document.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/upsert-document.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "client", + "server", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Session": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Document data as JSON object. Include all required attributes of the document to be created or updated.", + "x-example": "{}" + }, + "permissions": { + "type": "array", + "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", + "x-example": "[\"read(\"any\")\"]", + "items": { + "type": "string" + } + } + }, + "required": [ + "data" + ] + } + } + } + } + }, "patch": { "summary": "Update document", "operationId": "databasesUpdateDocument", @@ -8122,7 +8242,7 @@ "x-appwrite": { "method": "deleteDocument", "group": "documents", - "weight": 116, + "weight": 119, "cookies": false, "type": "", "deprecated": false, @@ -8185,6 +8305,240 @@ ] } }, + "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/decrement": { + "patch": { + "summary": "Decrement document attribute", + "operationId": "databasesDecrementDocumentAttribute", + "tags": [ + "databases" + ], + "description": "Decrement a specific attribute of a document by a given value.", + "responses": { + "200": { + "description": "Document", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/document" + } + } + } + } + }, + "x-appwrite": { + "method": "decrementDocumentAttribute", + "group": "documents", + "weight": 116, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/decrement-document-attribute.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/decrement-document-attribute.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "console", + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "attribute", + "description": "Attribute key.", + "required": true, + "schema": { + "type": "string" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "value": { + "type": "number", + "description": "Value to decrement the attribute by. The value must be a number.", + "x-example": null + }, + "min": { + "type": "number", + "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", + "x-example": null + } + } + } + } + } + } + } + }, + "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/increment": { + "patch": { + "summary": "Increment document attribute", + "operationId": "databasesIncrementDocumentAttribute", + "tags": [ + "databases" + ], + "description": "Increment a specific attribute of a document by a given value.", + "responses": { + "200": { + "description": "Document", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/document" + } + } + } + } + }, + "x-appwrite": { + "method": "incrementDocumentAttribute", + "group": "documents", + "weight": 115, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/increment-document-attribute.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/increment-document-attribute.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "console", + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + }, + { + "name": "attribute", + "description": "Attribute key.", + "required": true, + "schema": { + "type": "string" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "value": { + "type": "number", + "description": "Value to increment the attribute by. The value must be a number.", + "x-example": null + }, + "max": { + "type": "number", + "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", + "x-example": null + } + } + } + } + } + } + } + }, "\/databases\/{databaseId}\/collections\/{collectionId}\/indexes": { "get": { "summary": "List indexes", @@ -8573,7 +8927,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 375, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -8647,7 +9001,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 372, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -8730,6 +9084,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -8755,7 +9110,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -8879,7 +9235,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 377, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -8929,7 +9285,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 378, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -8980,7 +9336,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 373, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -9040,7 +9396,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 374, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -9130,6 +9486,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -9155,7 +9512,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -9269,7 +9627,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 376, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -9331,7 +9689,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 381, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -9412,7 +9770,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 382, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -9496,7 +9854,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 379, + "weight": 382, "cookies": false, "type": "upload", "deprecated": false, @@ -9593,7 +9951,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 387, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -9679,7 +10037,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 384, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -9783,7 +10141,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 385, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -9881,7 +10239,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 380, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -9944,7 +10302,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 383, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -10009,7 +10367,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 386, + "weight": 389, "cookies": false, "type": "location", "deprecated": false, @@ -10100,7 +10458,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 388, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -10172,7 +10530,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 391, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -10249,7 +10607,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 389, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -10366,7 +10724,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 390, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -10433,7 +10791,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 392, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -10505,7 +10863,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 397, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -10565,7 +10923,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 395, + "weight": 398, "cookies": false, "type": "", "deprecated": false, @@ -10657,7 +11015,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 396, + "weight": 399, "cookies": false, "type": "", "deprecated": false, @@ -10727,7 +11085,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 398, + "weight": 401, "cookies": false, "type": "", "deprecated": false, @@ -10819,7 +11177,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 399, + "weight": 402, "cookies": false, "type": "", "deprecated": false, @@ -10891,7 +11249,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 305, + "weight": 308, "cookies": false, "type": "graphql", "deprecated": false, @@ -10945,7 +11303,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 304, + "weight": 307, "cookies": false, "type": "graphql", "deprecated": false, @@ -10999,7 +11357,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 129, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -11049,7 +11407,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 150, + "weight": 153, "cookies": false, "type": "", "deprecated": false, @@ -11099,7 +11457,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 132, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -11149,7 +11507,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 137, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -11210,7 +11568,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 131, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -11260,7 +11618,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 133, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -11310,7 +11668,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 139, + "weight": 142, "cookies": false, "type": "", "deprecated": false, @@ -11373,7 +11731,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 138, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -11436,7 +11794,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 140, + "weight": 143, "cookies": false, "type": "", "deprecated": false, @@ -11510,7 +11868,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 141, + "weight": 144, "cookies": false, "type": "", "deprecated": false, @@ -11573,7 +11931,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 151, + "weight": 154, "cookies": false, "type": "", "deprecated": false, @@ -11662,7 +12020,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 145, + "weight": 148, "cookies": false, "type": "", "deprecated": false, @@ -11725,7 +12083,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 136, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -11788,7 +12146,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 142, + "weight": 145, "cookies": false, "type": "", "deprecated": false, @@ -11851,7 +12209,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 143, + "weight": 146, "cookies": false, "type": "", "deprecated": false, @@ -11914,7 +12272,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 144, + "weight": 147, "cookies": false, "type": "", "deprecated": false, @@ -11977,7 +12335,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 146, + "weight": 149, "cookies": false, "type": "", "deprecated": false, @@ -12040,7 +12398,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 147, + "weight": 150, "cookies": false, "type": "", "deprecated": false, @@ -12103,7 +12461,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 135, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -12166,7 +12524,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 149, + "weight": 152, "cookies": false, "type": "", "deprecated": false, @@ -12216,7 +12574,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 148, + "weight": 151, "cookies": false, "type": "", "deprecated": false, @@ -12266,7 +12624,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 134, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -12316,7 +12674,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 121, + "weight": 124, "cookies": false, "type": "", "deprecated": false, @@ -12370,7 +12728,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 122, + "weight": 125, "cookies": false, "type": "", "deprecated": false, @@ -12424,7 +12782,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 126, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -12478,7 +12836,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 123, + "weight": 126, "cookies": false, "type": "", "deprecated": false, @@ -12532,7 +12890,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 124, + "weight": 127, "cookies": false, "type": "", "deprecated": false, @@ -12586,7 +12944,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 125, + "weight": 128, "cookies": false, "type": "", "deprecated": false, @@ -12640,7 +12998,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 127, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -12694,7 +13052,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 128, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -12748,7 +13106,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 359, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -12825,7 +13183,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 356, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -12970,7 +13328,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 363, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -13117,7 +13475,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 358, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -13292,7 +13650,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 365, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -13471,7 +13829,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 357, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -13581,7 +13939,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 364, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -13694,7 +14052,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 362, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -13748,7 +14106,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 366, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -13811,7 +14169,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 360, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -13887,7 +14245,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 361, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -13963,7 +14321,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 331, + "weight": 334, "cookies": false, "type": "", "deprecated": false, @@ -14040,7 +14398,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 330, + "weight": 333, "cookies": false, "type": "", "deprecated": false, @@ -14146,7 +14504,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 343, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -14255,7 +14613,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 329, + "weight": 332, "cookies": false, "type": "", "deprecated": false, @@ -14341,7 +14699,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 342, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -14430,7 +14788,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 321, + "weight": 324, "cookies": false, "type": "", "deprecated": false, @@ -14546,7 +14904,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 334, + "weight": 337, "cookies": false, "type": "", "deprecated": false, @@ -14665,7 +15023,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 324, + "weight": 327, "cookies": false, "type": "", "deprecated": false, @@ -14761,7 +15119,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 337, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -14860,7 +15218,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 322, + "weight": 325, "cookies": false, "type": "", "deprecated": false, @@ -14966,7 +15324,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 335, + "weight": 338, "cookies": false, "type": "", "deprecated": false, @@ -15075,7 +15433,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 323, + "weight": 326, "cookies": false, "type": "", "deprecated": false, @@ -15219,7 +15577,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 336, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -15365,7 +15723,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 325, + "weight": 328, "cookies": false, "type": "", "deprecated": false, @@ -15461,7 +15819,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 338, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -15560,7 +15918,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 326, + "weight": 329, "cookies": false, "type": "", "deprecated": false, @@ -15656,7 +16014,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 339, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -15755,7 +16113,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 327, + "weight": 330, "cookies": false, "type": "", "deprecated": false, @@ -15851,7 +16209,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 340, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -15950,7 +16308,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 328, + "weight": 331, "cookies": false, "type": "", "deprecated": false, @@ -16046,7 +16404,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 341, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -16145,7 +16503,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 333, + "weight": 336, "cookies": false, "type": "", "deprecated": false, @@ -16199,7 +16557,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 344, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -16262,7 +16620,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 332, + "weight": 335, "cookies": false, "type": "", "deprecated": false, @@ -16338,7 +16696,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 353, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -16414,7 +16772,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 346, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -16489,7 +16847,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 345, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -16573,7 +16931,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 348, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -16634,7 +16992,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 349, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -16712,7 +17070,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 350, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -16775,7 +17133,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 347, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -16851,7 +17209,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 352, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -16936,7 +17294,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 351, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -17028,7 +17386,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 354, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -17092,7 +17450,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 355, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -17169,7 +17527,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 404, + "weight": 407, "cookies": false, "type": "", "deprecated": false, @@ -17240,7 +17598,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 402, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -17376,6 +17734,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -17401,7 +17760,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -17488,7 +17848,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 407, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -17538,7 +17898,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 430, + "weight": 433, "cookies": false, "type": "", "deprecated": false, @@ -17589,7 +17949,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 403, + "weight": 406, "cookies": false, "type": "", "deprecated": false, @@ -17649,7 +18009,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 405, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -17792,6 +18152,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -17817,7 +18178,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -17893,7 +18255,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 406, + "weight": 409, "cookies": false, "type": "", "deprecated": false, @@ -17955,7 +18317,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 413, + "weight": 416, "cookies": false, "type": "", "deprecated": false, @@ -18036,7 +18398,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 412, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -18120,7 +18482,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 408, + "weight": 411, "cookies": false, "type": "upload", "deprecated": false, @@ -18222,7 +18584,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 416, + "weight": 419, "cookies": false, "type": "", "deprecated": false, @@ -18303,7 +18665,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 409, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -18407,7 +18769,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 410, + "weight": 413, "cookies": false, "type": "", "deprecated": false, @@ -18506,7 +18868,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 411, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -18569,7 +18931,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 414, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -18634,7 +18996,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 415, + "weight": 418, "cookies": false, "type": "location", "deprecated": false, @@ -18725,7 +19087,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 417, + "weight": 420, "cookies": false, "type": "", "deprecated": false, @@ -18797,7 +19159,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 419, + "weight": 422, "cookies": false, "type": "", "deprecated": false, @@ -18869,7 +19231,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 418, + "weight": 421, "cookies": false, "type": "", "deprecated": false, @@ -18932,7 +19294,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 420, + "weight": 423, "cookies": false, "type": "", "deprecated": false, @@ -19004,7 +19366,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 423, + "weight": 426, "cookies": false, "type": "", "deprecated": false, @@ -19064,7 +19426,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 421, + "weight": 424, "cookies": false, "type": "", "deprecated": false, @@ -19156,7 +19518,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 422, + "weight": 425, "cookies": false, "type": "", "deprecated": false, @@ -19226,7 +19588,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 424, + "weight": 427, "cookies": false, "type": "", "deprecated": false, @@ -19318,7 +19680,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 425, + "weight": 428, "cookies": false, "type": "", "deprecated": false, @@ -19390,7 +19752,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 206, + "weight": 209, "cookies": false, "type": "", "deprecated": false, @@ -19464,7 +19826,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 205, + "weight": 208, "cookies": false, "type": "", "deprecated": false, @@ -19592,7 +19954,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 207, + "weight": 210, "cookies": false, "type": "", "deprecated": false, @@ -19652,7 +20014,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 208, + "weight": 211, "cookies": false, "type": "", "deprecated": false, @@ -19777,7 +20139,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 209, + "weight": 212, "cookies": false, "type": "", "deprecated": false, @@ -19839,7 +20201,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 211, + "weight": 214, "cookies": false, "type": "", "deprecated": false, @@ -19927,7 +20289,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 210, + "weight": 213, "cookies": false, "type": "upload", "deprecated": false, @@ -20027,7 +20389,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 212, + "weight": 215, "cookies": false, "type": "", "deprecated": false, @@ -20101,7 +20463,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 217, + "weight": 220, "cookies": false, "type": "", "deprecated": false, @@ -20192,7 +20554,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 218, + "weight": 221, "cookies": false, "type": "", "deprecated": false, @@ -20261,7 +20623,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 214, + "weight": 217, "cookies": false, "type": "location", "deprecated": false, @@ -20341,7 +20703,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 213, + "weight": 216, "cookies": false, "type": "location", "deprecated": false, @@ -20532,7 +20894,8 @@ "png", "webp", "heic", - "avif" + "avif", + "gif" ], "x-enum-name": "ImageFormat", "x-enum-keys": [], @@ -20570,7 +20933,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 215, + "weight": 218, "cookies": false, "type": "location", "deprecated": false, @@ -20657,7 +21020,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 222, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -20735,7 +21098,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 221, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -20822,7 +21185,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 223, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -20886,7 +21249,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 225, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -20962,7 +21325,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 227, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -21028,7 +21391,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 229, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -21116,7 +21479,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 228, + "weight": 231, "cookies": false, "type": "", "deprecated": false, @@ -21229,7 +21592,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 230, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -21303,7 +21666,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 231, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -21392,7 +21755,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 233, + "weight": 236, "cookies": false, "type": "", "deprecated": false, @@ -21468,7 +21831,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 232, + "weight": 235, "cookies": false, "type": "", "deprecated": false, @@ -21567,7 +21930,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 224, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -21629,7 +21992,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 226, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -21712,7 +22075,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 438, + "weight": 441, "cookies": false, "type": "", "deprecated": false, @@ -21777,7 +22140,7 @@ "tags": [ "tokens" ], - "description": "Create a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "description": "Create a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "responses": { "201": { "description": "ResourceToken", @@ -21793,12 +22156,12 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 436, + "weight": 439, "cookies": false, "type": "", "deprecated": false, "demo": "tokens\/create-file-token.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "rate-limit": 60, "rate-time": 60, "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", @@ -21883,7 +22246,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 437, + "weight": 440, "cookies": false, "type": "", "deprecated": false, @@ -21944,7 +22307,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 439, + "weight": 442, "cookies": false, "type": "", "deprecated": false, @@ -22015,7 +22378,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 440, + "weight": 443, "cookies": false, "type": "", "deprecated": false, @@ -22078,7 +22441,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 244, + "weight": 247, "cookies": false, "type": "", "deprecated": false, @@ -22152,7 +22515,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 235, + "weight": 238, "cookies": false, "type": "", "deprecated": false, @@ -22241,7 +22604,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 238, + "weight": 241, "cookies": false, "type": "", "deprecated": false, @@ -22327,7 +22690,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 236, + "weight": 239, "cookies": false, "type": "", "deprecated": false, @@ -22413,7 +22776,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 252, + "weight": 255, "cookies": false, "type": "", "deprecated": false, @@ -22482,7 +22845,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 275, + "weight": 278, "cookies": false, "type": "", "deprecated": false, @@ -22544,7 +22907,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 237, + "weight": 240, "cookies": false, "type": "", "deprecated": false, @@ -22630,7 +22993,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 240, + "weight": 243, "cookies": false, "type": "", "deprecated": false, @@ -22716,7 +23079,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 241, + "weight": 244, "cookies": false, "type": "", "deprecated": false, @@ -22832,7 +23195,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 242, + "weight": 245, "cookies": false, "type": "", "deprecated": false, @@ -22936,7 +23299,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 239, + "weight": 242, "cookies": false, "type": "", "deprecated": false, @@ -23042,7 +23405,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 245, + "weight": 248, "cookies": false, "type": "", "deprecated": false, @@ -23095,7 +23458,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 273, + "weight": 276, "cookies": false, "type": "", "deprecated": false, @@ -23157,7 +23520,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 258, + "weight": 261, "cookies": false, "type": "", "deprecated": false, @@ -23238,7 +23601,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 276, + "weight": 279, "cookies": false, "type": "", "deprecated": false, @@ -23321,7 +23684,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 254, + "weight": 257, "cookies": false, "type": "", "deprecated": false, @@ -23405,7 +23768,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 250, + "weight": 253, "cookies": false, "type": "", "deprecated": false, @@ -23480,7 +23843,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 249, + "weight": 252, "cookies": false, "type": "", "deprecated": false, @@ -23566,7 +23929,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 263, + "weight": 266, "cookies": false, "type": "", "deprecated": false, @@ -23640,7 +24003,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 268, + "weight": 271, "cookies": false, "type": "", "deprecated": false, @@ -23717,7 +24080,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 264, + "weight": 267, "cookies": false, "type": "", "deprecated": false, @@ -23779,7 +24142,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 265, + "weight": 268, "cookies": false, "type": "", "deprecated": false, @@ -23839,7 +24202,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 267, + "weight": 270, "cookies": false, "type": "", "deprecated": false, @@ -23899,7 +24262,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 266, + "weight": 269, "cookies": false, "type": "", "deprecated": false, @@ -23961,7 +24324,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 256, + "weight": 259, "cookies": false, "type": "", "deprecated": false, @@ -24042,7 +24405,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 257, + "weight": 260, "cookies": false, "type": "", "deprecated": false, @@ -24123,7 +24486,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 259, + "weight": 262, "cookies": false, "type": "", "deprecated": false, @@ -24204,7 +24567,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 246, + "weight": 249, "cookies": false, "type": "", "deprecated": false, @@ -24264,7 +24627,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 261, + "weight": 264, "cookies": false, "type": "", "deprecated": false, @@ -24345,7 +24708,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 248, + "weight": 251, "cookies": false, "type": "", "deprecated": false, @@ -24405,7 +24768,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 269, + "weight": 272, "cookies": false, "type": "", "deprecated": false, @@ -24458,7 +24821,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 272, + "weight": 275, "cookies": false, "type": "", "deprecated": false, @@ -24513,7 +24876,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 271, + "weight": 274, "cookies": false, "type": "", "deprecated": false, @@ -24585,7 +24948,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 253, + "weight": 256, "cookies": false, "type": "", "deprecated": false, @@ -24666,7 +25029,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 251, + "weight": 254, "cookies": false, "type": "", "deprecated": false, @@ -24705,7 +25068,7 @@ }, { "name": "queries", - "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: name, email, phone, status, passwordUpdate, registration, emailVerification, phoneVerification, labels", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, providerId, identifier, providerType", "required": false, "schema": { "type": "array", @@ -24740,7 +25103,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 243, + "weight": 246, "cookies": false, "type": "", "deprecated": false, @@ -24851,7 +25214,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 247, + "weight": 250, "cookies": false, "type": "", "deprecated": false, @@ -24922,7 +25285,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 262, + "weight": 265, "cookies": false, "type": "", "deprecated": false, @@ -25012,7 +25375,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 274, + "weight": 277, "cookies": false, "type": "", "deprecated": false, @@ -25085,7 +25448,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 270, + "weight": 273, "cookies": false, "type": "", "deprecated": false, @@ -25168,7 +25531,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 260, + "weight": 263, "cookies": false, "type": "", "deprecated": false, @@ -25249,7 +25612,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 255, + "weight": 258, "cookies": false, "type": "", "deprecated": false, @@ -26444,6 +26807,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "nullable": true } }, "required": [ @@ -27195,6 +27564,12 @@ "description": "Document ID.", "x-example": "5e5ea5c16897e" }, + "$sequence": { + "type": "integer", + "description": "Document automatically incrementing ID.", + "x-example": 1, + "format": "int32" + }, "$collectionId": { "type": "string", "description": "Collection ID.", @@ -27229,6 +27604,7 @@ "additionalProperties": true, "required": [ "$id", + "$sequence", "$collectionId", "$databaseId", "$createdAt", diff --git a/app/config/specs/swagger2-1.7.x-client.json b/app/config/specs/swagger2-1.7.x-client.json index 9bfe261df1..92132151b4 100644 --- a/app/config/specs/swagger2-1.7.x-client.json +++ b/app/config/specs/swagger2-1.7.x-client.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "version": "1.7.0", + "version": "1.7.4", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -61,6 +61,12 @@ "name": "X-Appwrite-Session", "description": "The user session to authenticate with", "in": "header" + }, + "DevKey": { + "type": "apiKey", + "name": "X-Appwrite-Dev-Key", + "description": "Your secret dev API key", + "in": "header" } }, "paths": { @@ -4791,6 +4797,111 @@ } ] }, + "put": { + "summary": "Upsert document", + "operationId": "databasesUpsertDocument", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "responses": { + "200": { + "description": "Document", + "schema": { + "$ref": "#\/definitions\/document" + } + } + }, + "x-appwrite": { + "method": "upsertDocument", + "group": "documents", + "weight": 114, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/upsert-document.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/upsert-document.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "client", + "server", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Document data as JSON object. Include all required attributes of the document to be created or updated.", + "default": {}, + "x-example": "{}" + }, + "permissions": { + "type": "array", + "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", + "default": null, + "x-example": "[\"read(\"any\")\"]", + "items": { + "type": "string" + } + } + }, + "required": [ + "data" + ] + } + } + ] + }, "patch": { "summary": "Update document", "operationId": "databasesUpdateDocument", @@ -4912,7 +5023,7 @@ "x-appwrite": { "method": "deleteDocument", "group": "documents", - "weight": 116, + "weight": 117, "cookies": false, "type": "", "deprecated": false, @@ -4990,7 +5101,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 391, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -5064,7 +5175,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 389, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -5181,7 +5292,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 390, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -5253,7 +5364,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 305, + "weight": 306, "cookies": false, "type": "graphql", "deprecated": false, @@ -5327,7 +5438,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 304, + "weight": 305, "cookies": false, "type": "graphql", "deprecated": false, @@ -5399,7 +5510,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 121, + "weight": 122, "cookies": false, "type": "", "deprecated": false, @@ -5451,7 +5562,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 122, + "weight": 123, "cookies": false, "type": "", "deprecated": false, @@ -5503,7 +5614,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 126, + "weight": 127, "cookies": false, "type": "", "deprecated": false, @@ -5555,7 +5666,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 123, + "weight": 124, "cookies": false, "type": "", "deprecated": false, @@ -5607,7 +5718,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 124, + "weight": 125, "cookies": false, "type": "", "deprecated": false, @@ -5659,7 +5770,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 125, + "weight": 126, "cookies": false, "type": "", "deprecated": false, @@ -5711,7 +5822,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 127, + "weight": 128, "cookies": false, "type": "", "deprecated": false, @@ -5763,7 +5874,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 128, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -5817,7 +5928,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 351, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -5902,7 +6013,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 355, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -5973,7 +6084,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 211, + "weight": 212, "cookies": false, "type": "", "deprecated": false, @@ -6056,7 +6167,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 210, + "weight": 211, "cookies": false, "type": "upload", "deprecated": false, @@ -6146,7 +6257,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 212, + "weight": 213, "cookies": false, "type": "", "deprecated": false, @@ -6216,7 +6327,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 217, + "weight": 218, "cookies": false, "type": "", "deprecated": false, @@ -6305,7 +6416,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 218, + "weight": 219, "cookies": false, "type": "", "deprecated": false, @@ -6375,7 +6486,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 214, + "weight": 215, "cookies": false, "type": "location", "deprecated": false, @@ -6454,7 +6565,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 213, + "weight": 214, "cookies": false, "type": "location", "deprecated": false, @@ -6660,7 +6771,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 215, + "weight": 216, "cookies": false, "type": "location", "deprecated": false, @@ -6739,7 +6850,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 222, + "weight": 223, "cookies": false, "type": "", "deprecated": false, @@ -6814,7 +6925,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 221, + "weight": 222, "cookies": false, "type": "", "deprecated": false, @@ -6904,7 +7015,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 223, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -6966,7 +7077,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 225, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -7041,7 +7152,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 227, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -7103,7 +7214,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 229, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -7186,7 +7297,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 228, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -7299,7 +7410,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 230, + "weight": 231, "cookies": false, "type": "", "deprecated": false, @@ -7369,7 +7480,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 231, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -7455,7 +7566,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 233, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -7527,7 +7638,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 232, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -7621,7 +7732,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 224, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -7682,7 +7793,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 226, + "weight": 227, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/swagger2-1.7.x-console.json b/app/config/specs/swagger2-1.7.x-console.json index d7f8a4c9d1..e53a0dfb0b 100644 --- a/app/config/specs/swagger2-1.7.x-console.json +++ b/app/config/specs/swagger2-1.7.x-console.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "version": "1.7.0", + "version": "1.7.4", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -4527,7 +4527,7 @@ "x-appwrite": { "method": "chat", "group": "console", - "weight": 307, + "weight": 308, "cookies": false, "type": "", "deprecated": false, @@ -4590,7 +4590,7 @@ "x-appwrite": { "method": "getResource", "group": null, - "weight": 431, + "weight": 432, "cookies": false, "type": "", "deprecated": false, @@ -4661,7 +4661,7 @@ "x-appwrite": { "method": "variables", "group": "console", - "weight": 306, + "weight": 307, "cookies": false, "type": "", "deprecated": false, @@ -4863,7 +4863,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 118, + "weight": 119, "cookies": false, "type": "", "deprecated": false, @@ -8296,7 +8296,7 @@ "x-appwrite": { "method": "upsertDocuments", "group": "documents", - "weight": 115, + "weight": 116, "cookies": false, "type": "", "deprecated": false, @@ -8346,13 +8346,16 @@ "documents": { "type": "array", "description": "Array of document data as JSON objects. May contain partial documents.", - "default": [], + "default": null, "x-example": null, "items": { "type": "object" } } - } + }, + "required": [ + "documents" + ] } } ] @@ -8381,7 +8384,7 @@ "x-appwrite": { "method": "updateDocuments", "group": "documents", - "weight": 114, + "weight": 115, "cookies": false, "type": "", "deprecated": false, @@ -8472,7 +8475,7 @@ "x-appwrite": { "method": "deleteDocuments", "group": "documents", - "weight": 117, + "weight": 118, "cookies": false, "type": "", "deprecated": false, @@ -8623,6 +8626,111 @@ } ] }, + "put": { + "summary": "Upsert document", + "operationId": "databasesUpsertDocument", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "responses": { + "200": { + "description": "Document", + "schema": { + "$ref": "#\/definitions\/document" + } + } + }, + "x-appwrite": { + "method": "upsertDocument", + "group": "documents", + "weight": 114, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/upsert-document.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/upsert-document.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "client", + "server", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Document data as JSON object. Include all required attributes of the document to be created or updated.", + "default": {}, + "x-example": "{}" + }, + "permissions": { + "type": "array", + "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", + "default": null, + "x-example": "[\"read(\"any\")\"]", + "items": { + "type": "string" + } + } + }, + "required": [ + "data" + ] + } + } + ] + }, "patch": { "summary": "Update document", "operationId": "databasesUpdateDocument", @@ -8744,7 +8852,7 @@ "x-appwrite": { "method": "deleteDocument", "group": "documents", - "weight": 116, + "weight": 117, "cookies": false, "type": "", "deprecated": false, @@ -9335,7 +9443,7 @@ "x-appwrite": { "method": "getCollectionUsage", "group": null, - "weight": 120, + "weight": 121, "cookies": false, "type": "", "deprecated": false, @@ -9491,7 +9599,7 @@ "x-appwrite": { "method": "getDatabaseUsage", "group": null, - "weight": 119, + "weight": 120, "cookies": false, "type": "", "deprecated": false, @@ -9569,7 +9677,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 375, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -9641,7 +9749,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 372, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -9890,7 +9998,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 377, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -9939,7 +10047,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 378, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -9989,7 +10097,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 401, + "weight": 402, "cookies": false, "type": "", "deprecated": false, @@ -10083,7 +10191,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 400, + "weight": 401, "cookies": false, "type": "", "deprecated": false, @@ -10141,7 +10249,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 394, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -10211,7 +10319,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 373, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -10270,7 +10378,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 374, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -10515,7 +10623,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 376, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -10576,7 +10684,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 381, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -10653,7 +10761,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 382, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -10733,7 +10841,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 379, + "weight": 380, "cookies": false, "type": "upload", "deprecated": false, @@ -10825,7 +10933,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 387, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -10910,7 +11018,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 384, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -11016,7 +11124,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 385, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -11112,7 +11220,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 380, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -11174,7 +11282,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 383, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -11241,7 +11349,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 386, + "weight": 387, "cookies": false, "type": "location", "deprecated": false, @@ -11327,7 +11435,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 388, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -11394,7 +11502,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 391, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -11468,7 +11576,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 389, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -11585,7 +11693,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 390, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -11650,7 +11758,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 392, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -11717,7 +11825,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 393, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -11795,7 +11903,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 397, + "weight": 398, "cookies": false, "type": "", "deprecated": false, @@ -11854,7 +11962,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 395, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -11944,7 +12052,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 396, + "weight": 397, "cookies": false, "type": "", "deprecated": false, @@ -12011,7 +12119,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 398, + "weight": 399, "cookies": false, "type": "", "deprecated": false, @@ -12103,7 +12211,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 399, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -12172,7 +12280,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 305, + "weight": 306, "cookies": false, "type": "graphql", "deprecated": false, @@ -12246,7 +12354,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 304, + "weight": 305, "cookies": false, "type": "graphql", "deprecated": false, @@ -12318,7 +12426,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 129, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -12367,7 +12475,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 150, + "weight": 151, "cookies": false, "type": "", "deprecated": false, @@ -12416,7 +12524,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 132, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -12465,7 +12573,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 137, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -12523,7 +12631,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 131, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -12572,7 +12680,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 133, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -12621,7 +12729,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 139, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -12681,7 +12789,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 138, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -12741,7 +12849,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 140, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -12810,7 +12918,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 141, + "weight": 142, "cookies": false, "type": "", "deprecated": false, @@ -12870,7 +12978,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 151, + "weight": 152, "cookies": false, "type": "", "deprecated": false, @@ -12954,7 +13062,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 145, + "weight": 146, "cookies": false, "type": "", "deprecated": false, @@ -13014,7 +13122,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 136, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -13074,7 +13182,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 142, + "weight": 143, "cookies": false, "type": "", "deprecated": false, @@ -13134,7 +13242,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 143, + "weight": 144, "cookies": false, "type": "", "deprecated": false, @@ -13194,7 +13302,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 144, + "weight": 145, "cookies": false, "type": "", "deprecated": false, @@ -13254,7 +13362,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 146, + "weight": 147, "cookies": false, "type": "", "deprecated": false, @@ -13314,7 +13422,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 147, + "weight": 148, "cookies": false, "type": "", "deprecated": false, @@ -13374,7 +13482,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 135, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -13434,7 +13542,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 149, + "weight": 150, "cookies": false, "type": "", "deprecated": false, @@ -13483,7 +13591,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 148, + "weight": 149, "cookies": false, "type": "", "deprecated": false, @@ -13532,7 +13640,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 134, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -13581,7 +13689,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 121, + "weight": 122, "cookies": false, "type": "", "deprecated": false, @@ -13633,7 +13741,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 122, + "weight": 123, "cookies": false, "type": "", "deprecated": false, @@ -13685,7 +13793,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 126, + "weight": 127, "cookies": false, "type": "", "deprecated": false, @@ -13737,7 +13845,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 123, + "weight": 124, "cookies": false, "type": "", "deprecated": false, @@ -13789,7 +13897,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 124, + "weight": 125, "cookies": false, "type": "", "deprecated": false, @@ -13841,7 +13949,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 125, + "weight": 126, "cookies": false, "type": "", "deprecated": false, @@ -13893,7 +14001,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 127, + "weight": 128, "cookies": false, "type": "", "deprecated": false, @@ -13945,7 +14053,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 128, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -13997,7 +14105,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 359, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -14072,7 +14180,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 356, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -14230,7 +14338,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 363, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -14385,7 +14493,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 358, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -14580,7 +14688,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 365, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -14774,7 +14882,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 357, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -14892,7 +15000,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 364, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -15006,7 +15114,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 362, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -15061,7 +15169,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 366, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -15121,7 +15229,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 360, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -15193,7 +15301,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 361, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -15265,7 +15373,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 331, + "weight": 332, "cookies": false, "type": "", "deprecated": false, @@ -15340,7 +15448,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 330, + "weight": 331, "cookies": false, "type": "", "deprecated": false, @@ -15455,7 +15563,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 343, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -15568,7 +15676,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 329, + "weight": 330, "cookies": false, "type": "", "deprecated": false, @@ -15659,7 +15767,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 342, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -15748,7 +15856,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 321, + "weight": 322, "cookies": false, "type": "", "deprecated": false, @@ -15875,7 +15983,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 334, + "weight": 335, "cookies": false, "type": "", "deprecated": false, @@ -16000,7 +16108,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 324, + "weight": 325, "cookies": false, "type": "", "deprecated": false, @@ -16103,7 +16211,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 337, + "weight": 338, "cookies": false, "type": "", "deprecated": false, @@ -16204,7 +16312,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 322, + "weight": 323, "cookies": false, "type": "", "deprecated": false, @@ -16319,7 +16427,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 335, + "weight": 336, "cookies": false, "type": "", "deprecated": false, @@ -16432,7 +16540,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 323, + "weight": 324, "cookies": false, "type": "", "deprecated": false, @@ -16591,7 +16699,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 336, + "weight": 337, "cookies": false, "type": "", "deprecated": false, @@ -16747,7 +16855,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 325, + "weight": 326, "cookies": false, "type": "", "deprecated": false, @@ -16850,7 +16958,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 338, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -16951,7 +17059,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 326, + "weight": 327, "cookies": false, "type": "", "deprecated": false, @@ -17054,7 +17162,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 339, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -17155,7 +17263,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 327, + "weight": 328, "cookies": false, "type": "", "deprecated": false, @@ -17258,7 +17366,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 340, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -17359,7 +17467,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 328, + "weight": 329, "cookies": false, "type": "", "deprecated": false, @@ -17462,7 +17570,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 341, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -17561,7 +17669,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 333, + "weight": 334, "cookies": false, "type": "", "deprecated": false, @@ -17616,7 +17724,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 344, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -17676,7 +17784,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 332, + "weight": 333, "cookies": false, "type": "", "deprecated": false, @@ -17748,7 +17856,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 353, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -17820,7 +17928,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 346, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -17893,7 +18001,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 345, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -17981,7 +18089,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 348, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -18041,7 +18149,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 349, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -18120,7 +18228,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 350, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -18180,7 +18288,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 347, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -18252,7 +18360,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 352, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -18333,7 +18441,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 351, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -18421,7 +18529,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 354, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -18484,7 +18592,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 355, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -18555,7 +18663,7 @@ "x-appwrite": { "method": "list", "group": null, - "weight": 313, + "weight": 314, "cookies": false, "type": "", "deprecated": false, @@ -18628,7 +18736,7 @@ "x-appwrite": { "method": "createAppwriteMigration", "group": null, - "weight": 308, + "weight": 309, "cookies": false, "type": "", "deprecated": false, @@ -18720,7 +18828,7 @@ "x-appwrite": { "method": "getAppwriteReport", "group": null, - "weight": 315, + "weight": 316, "cookies": false, "type": "", "deprecated": false, @@ -18808,7 +18916,7 @@ "x-appwrite": { "method": "createCsvMigration", "group": null, - "weight": 312, + "weight": 313, "cookies": false, "type": "", "deprecated": false, @@ -18892,7 +19000,7 @@ "x-appwrite": { "method": "createFirebaseMigration", "group": null, - "weight": 309, + "weight": 310, "cookies": false, "type": "", "deprecated": false, @@ -18970,7 +19078,7 @@ "x-appwrite": { "method": "getFirebaseReport", "group": null, - "weight": 316, + "weight": 317, "cookies": false, "type": "", "deprecated": false, @@ -19041,7 +19149,7 @@ "x-appwrite": { "method": "createNHostMigration", "group": null, - "weight": 311, + "weight": 312, "cookies": false, "type": "", "deprecated": false, @@ -19160,7 +19268,7 @@ "x-appwrite": { "method": "getNHostReport", "group": null, - "weight": 318, + "weight": 319, "cookies": false, "type": "", "deprecated": false, @@ -19280,7 +19388,7 @@ "x-appwrite": { "method": "createSupabaseMigration", "group": null, - "weight": 310, + "weight": 311, "cookies": false, "type": "", "deprecated": false, @@ -19392,7 +19500,7 @@ "x-appwrite": { "method": "getSupabaseReport", "group": null, - "weight": 317, + "weight": 318, "cookies": false, "type": "", "deprecated": false, @@ -19503,7 +19611,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 314, + "weight": 315, "cookies": false, "type": "", "deprecated": false, @@ -19561,7 +19669,7 @@ "x-appwrite": { "method": "retry", "group": null, - "weight": 319, + "weight": 320, "cookies": false, "type": "", "deprecated": false, @@ -19614,7 +19722,7 @@ "x-appwrite": { "method": "delete", "group": null, - "weight": 320, + "weight": 321, "cookies": false, "type": "", "deprecated": false, @@ -19672,7 +19780,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 199, + "weight": 200, "cookies": false, "type": "", "deprecated": false, @@ -19754,7 +19862,7 @@ "x-appwrite": { "method": "listVariables", "group": null, - "weight": 201, + "weight": 202, "cookies": false, "type": "", "deprecated": false, @@ -19802,7 +19910,7 @@ "x-appwrite": { "method": "createVariable", "group": null, - "weight": 200, + "weight": 201, "cookies": false, "type": "", "deprecated": false, @@ -19883,7 +19991,7 @@ "x-appwrite": { "method": "getVariable", "group": null, - "weight": 202, + "weight": 203, "cookies": false, "type": "", "deprecated": false, @@ -19941,7 +20049,7 @@ "x-appwrite": { "method": "updateVariable", "group": null, - "weight": 203, + "weight": 204, "cookies": false, "type": "", "deprecated": false, @@ -20024,7 +20132,7 @@ "x-appwrite": { "method": "deleteVariable", "group": null, - "weight": 204, + "weight": 205, "cookies": false, "type": "", "deprecated": false, @@ -20082,7 +20190,7 @@ "x-appwrite": { "method": "list", "group": "projects", - "weight": 154, + "weight": 155, "cookies": false, "type": "", "deprecated": false, @@ -20153,7 +20261,7 @@ "x-appwrite": { "method": "create", "group": "projects", - "weight": 153, + "weight": 154, "cookies": false, "type": "", "deprecated": false, @@ -20300,7 +20408,7 @@ "x-appwrite": { "method": "get", "group": "projects", - "weight": 155, + "weight": 156, "cookies": false, "type": "", "deprecated": false, @@ -20358,7 +20466,7 @@ "x-appwrite": { "method": "update", "group": "projects", - "weight": 156, + "weight": 157, "cookies": false, "type": "", "deprecated": false, @@ -20483,7 +20591,7 @@ "x-appwrite": { "method": "delete", "group": "projects", - "weight": 173, + "weight": 174, "cookies": false, "type": "", "deprecated": false, @@ -20543,7 +20651,7 @@ "x-appwrite": { "method": "updateApiStatus", "group": "projects", - "weight": 160, + "weight": 161, "cookies": false, "type": "", "deprecated": false, @@ -20635,7 +20743,7 @@ "x-appwrite": { "method": "updateApiStatusAll", "group": "projects", - "weight": 161, + "weight": 162, "cookies": false, "type": "", "deprecated": false, @@ -20713,7 +20821,7 @@ "x-appwrite": { "method": "updateAuthDuration", "group": "auth", - "weight": 166, + "weight": 167, "cookies": false, "type": "", "deprecated": false, @@ -20791,7 +20899,7 @@ "x-appwrite": { "method": "updateAuthLimit", "group": "auth", - "weight": 165, + "weight": 166, "cookies": false, "type": "", "deprecated": false, @@ -20869,7 +20977,7 @@ "x-appwrite": { "method": "updateAuthSessionsLimit", "group": "auth", - "weight": 171, + "weight": 172, "cookies": false, "type": "", "deprecated": false, @@ -20947,7 +21055,7 @@ "x-appwrite": { "method": "updateMembershipsPrivacy", "group": "auth", - "weight": 164, + "weight": 165, "cookies": false, "type": "", "deprecated": false, @@ -21039,7 +21147,7 @@ "x-appwrite": { "method": "updateMockNumbers", "group": "auth", - "weight": 172, + "weight": 173, "cookies": false, "type": "", "deprecated": false, @@ -21120,7 +21228,7 @@ "x-appwrite": { "method": "updateAuthPasswordDictionary", "group": "auth", - "weight": 169, + "weight": 170, "cookies": false, "type": "", "deprecated": false, @@ -21198,7 +21306,7 @@ "x-appwrite": { "method": "updateAuthPasswordHistory", "group": "auth", - "weight": 168, + "weight": 169, "cookies": false, "type": "", "deprecated": false, @@ -21276,7 +21384,7 @@ "x-appwrite": { "method": "updatePersonalDataCheck", "group": "auth", - "weight": 170, + "weight": 171, "cookies": false, "type": "", "deprecated": false, @@ -21354,7 +21462,7 @@ "x-appwrite": { "method": "updateSessionAlerts", "group": "auth", - "weight": 163, + "weight": 164, "cookies": false, "type": "", "deprecated": false, @@ -21432,7 +21540,7 @@ "x-appwrite": { "method": "updateAuthStatus", "group": "auth", - "weight": 167, + "weight": 168, "cookies": false, "type": "", "deprecated": false, @@ -21527,7 +21635,7 @@ "x-appwrite": { "method": "listDevKeys", "group": "devKeys", - "weight": 370, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -21597,7 +21705,7 @@ "x-appwrite": { "method": "createDevKey", "group": "devKeys", - "weight": 367, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -21680,7 +21788,7 @@ "x-appwrite": { "method": "getDevKey", "group": "devKeys", - "weight": 369, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -21746,7 +21854,7 @@ "x-appwrite": { "method": "updateDevKey", "group": "devKeys", - "weight": 368, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -21832,7 +21940,7 @@ "x-appwrite": { "method": "deleteDevKey", "group": "devKeys", - "weight": 371, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -21900,7 +22008,7 @@ "x-appwrite": { "method": "createJWT", "group": "auth", - "weight": 185, + "weight": 186, "cookies": false, "type": "", "deprecated": false, @@ -21985,7 +22093,7 @@ "x-appwrite": { "method": "listKeys", "group": "keys", - "weight": 181, + "weight": 182, "cookies": false, "type": "", "deprecated": false, @@ -22043,7 +22151,7 @@ "x-appwrite": { "method": "createKey", "group": "keys", - "weight": 180, + "weight": 181, "cookies": false, "type": "", "deprecated": false, @@ -22135,7 +22243,7 @@ "x-appwrite": { "method": "getKey", "group": "keys", - "weight": 182, + "weight": 183, "cookies": false, "type": "", "deprecated": false, @@ -22201,7 +22309,7 @@ "x-appwrite": { "method": "updateKey", "group": "keys", - "weight": 183, + "weight": 184, "cookies": false, "type": "", "deprecated": false, @@ -22296,7 +22404,7 @@ "x-appwrite": { "method": "deleteKey", "group": "keys", - "weight": 184, + "weight": 185, "cookies": false, "type": "", "deprecated": false, @@ -22364,7 +22472,7 @@ "x-appwrite": { "method": "updateOAuth2", "group": "auth", - "weight": 162, + "weight": 163, "cookies": false, "type": "", "deprecated": false, @@ -22502,7 +22610,7 @@ "x-appwrite": { "method": "listPlatforms", "group": "platforms", - "weight": 187, + "weight": 188, "cookies": false, "type": "", "deprecated": false, @@ -22560,7 +22668,7 @@ "x-appwrite": { "method": "createPlatform", "group": "platforms", - "weight": 186, + "weight": 187, "cookies": false, "type": "", "deprecated": false, @@ -22680,7 +22788,7 @@ "x-appwrite": { "method": "getPlatform", "group": "platforms", - "weight": 188, + "weight": 189, "cookies": false, "type": "", "deprecated": false, @@ -22746,7 +22854,7 @@ "x-appwrite": { "method": "updatePlatform", "group": "platforms", - "weight": 189, + "weight": 190, "cookies": false, "type": "", "deprecated": false, @@ -22843,7 +22951,7 @@ "x-appwrite": { "method": "deletePlatform", "group": "platforms", - "weight": 190, + "weight": 191, "cookies": false, "type": "", "deprecated": false, @@ -22911,7 +23019,7 @@ "x-appwrite": { "method": "updateServiceStatus", "group": "projects", - "weight": 158, + "weight": 159, "cookies": false, "type": "", "deprecated": false, @@ -23012,7 +23120,7 @@ "x-appwrite": { "method": "updateServiceStatusAll", "group": "projects", - "weight": 159, + "weight": 160, "cookies": false, "type": "", "deprecated": false, @@ -23090,7 +23198,7 @@ "x-appwrite": { "method": "updateSmtp", "group": "templates", - "weight": 191, + "weight": 192, "cookies": false, "type": "", "deprecated": false, @@ -23219,7 +23327,7 @@ "x-appwrite": { "method": "createSmtpTest", "group": "templates", - "weight": 192, + "weight": 193, "cookies": false, "type": "", "deprecated": false, @@ -23357,7 +23465,7 @@ "x-appwrite": { "method": "updateTeam", "group": "projects", - "weight": 157, + "weight": 158, "cookies": false, "type": "", "deprecated": false, @@ -23433,7 +23541,7 @@ "x-appwrite": { "method": "getEmailTemplate", "group": "templates", - "weight": 194, + "weight": 195, "cookies": false, "type": "", "deprecated": false, @@ -23653,7 +23761,7 @@ "x-appwrite": { "method": "updateEmailTemplate", "group": "templates", - "weight": 196, + "weight": 197, "cookies": false, "type": "", "deprecated": false, @@ -23916,7 +24024,7 @@ "x-appwrite": { "method": "deleteEmailTemplate", "group": "templates", - "weight": 198, + "weight": 199, "cookies": false, "type": "", "deprecated": false, @@ -24136,7 +24244,7 @@ "x-appwrite": { "method": "getSmsTemplate", "group": "templates", - "weight": 193, + "weight": 194, "cookies": false, "type": "", "deprecated": false, @@ -24353,7 +24461,7 @@ "x-appwrite": { "method": "updateSmsTemplate", "group": "templates", - "weight": 195, + "weight": 196, "cookies": false, "type": "", "deprecated": false, @@ -24588,7 +24696,7 @@ "x-appwrite": { "method": "deleteSmsTemplate", "group": "templates", - "weight": 197, + "weight": 198, "cookies": false, "type": "", "deprecated": false, @@ -24805,7 +24913,7 @@ "x-appwrite": { "method": "listWebhooks", "group": "webhooks", - "weight": 175, + "weight": 176, "cookies": false, "type": "", "deprecated": false, @@ -24863,7 +24971,7 @@ "x-appwrite": { "method": "createWebhook", "group": "webhooks", - "weight": 174, + "weight": 175, "cookies": false, "type": "", "deprecated": false, @@ -24981,7 +25089,7 @@ "x-appwrite": { "method": "getWebhook", "group": "webhooks", - "weight": 176, + "weight": 177, "cookies": false, "type": "", "deprecated": false, @@ -25047,7 +25155,7 @@ "x-appwrite": { "method": "updateWebhook", "group": "webhooks", - "weight": 177, + "weight": 178, "cookies": false, "type": "", "deprecated": false, @@ -25168,7 +25276,7 @@ "x-appwrite": { "method": "deleteWebhook", "group": "webhooks", - "weight": 179, + "weight": 180, "cookies": false, "type": "", "deprecated": false, @@ -25236,7 +25344,7 @@ "x-appwrite": { "method": "updateWebhookSignature", "group": "webhooks", - "weight": 178, + "weight": 179, "cookies": false, "type": "", "deprecated": false, @@ -25302,7 +25410,7 @@ "x-appwrite": { "method": "listRules", "group": null, - "weight": 291, + "weight": 292, "cookies": false, "type": "", "deprecated": false, @@ -25375,7 +25483,7 @@ "x-appwrite": { "method": "createAPIRule", "group": null, - "weight": 432, + "weight": 433, "cookies": false, "type": "", "deprecated": false, @@ -25445,7 +25553,7 @@ "x-appwrite": { "method": "createFunctionRule", "group": null, - "weight": 434, + "weight": 435, "cookies": false, "type": "", "deprecated": false, @@ -25528,7 +25636,7 @@ "x-appwrite": { "method": "createRedirectRule", "group": null, - "weight": 435, + "weight": 436, "cookies": false, "type": "", "deprecated": false, @@ -25625,7 +25733,7 @@ "x-appwrite": { "method": "createSiteRule", "group": null, - "weight": 433, + "weight": 434, "cookies": false, "type": "", "deprecated": false, @@ -25706,7 +25814,7 @@ "x-appwrite": { "method": "getRule", "group": null, - "weight": 292, + "weight": 293, "cookies": false, "type": "", "deprecated": false, @@ -25759,7 +25867,7 @@ "x-appwrite": { "method": "deleteRule", "group": null, - "weight": 293, + "weight": 294, "cookies": false, "type": "", "deprecated": false, @@ -25819,7 +25927,7 @@ "x-appwrite": { "method": "updateRuleVerification", "group": null, - "weight": 294, + "weight": 295, "cookies": false, "type": "", "deprecated": false, @@ -25877,7 +25985,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 404, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -25949,7 +26057,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 402, + "weight": 403, "cookies": false, "type": "", "deprecated": false, @@ -26214,7 +26322,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 407, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -26263,7 +26371,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 430, + "weight": 431, "cookies": false, "type": "", "deprecated": false, @@ -26313,7 +26421,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 426, + "weight": 427, "cookies": false, "type": "", "deprecated": false, @@ -26407,7 +26515,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 427, + "weight": 428, "cookies": false, "type": "", "deprecated": false, @@ -26465,7 +26573,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 428, + "weight": 429, "cookies": false, "type": "", "deprecated": false, @@ -26535,7 +26643,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 403, + "weight": 404, "cookies": false, "type": "", "deprecated": false, @@ -26594,7 +26702,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 405, + "weight": 406, "cookies": false, "type": "", "deprecated": false, @@ -26854,7 +26962,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 406, + "weight": 407, "cookies": false, "type": "", "deprecated": false, @@ -26915,7 +27023,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 413, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -26992,7 +27100,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 412, + "weight": 413, "cookies": false, "type": "", "deprecated": false, @@ -27072,7 +27180,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 408, + "weight": 409, "cookies": false, "type": "upload", "deprecated": false, @@ -27172,7 +27280,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 416, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -27251,7 +27359,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 409, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -27357,7 +27465,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 410, + "weight": 411, "cookies": false, "type": "", "deprecated": false, @@ -27454,7 +27562,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 411, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -27516,7 +27624,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 414, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -27583,7 +27691,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 415, + "weight": 416, "cookies": false, "type": "location", "deprecated": false, @@ -27669,7 +27777,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 417, + "weight": 418, "cookies": false, "type": "", "deprecated": false, @@ -27736,7 +27844,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 419, + "weight": 420, "cookies": false, "type": "", "deprecated": false, @@ -27807,7 +27915,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 418, + "weight": 419, "cookies": false, "type": "", "deprecated": false, @@ -27871,7 +27979,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 420, + "weight": 421, "cookies": false, "type": "", "deprecated": false, @@ -27938,7 +28046,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 429, + "weight": 430, "cookies": false, "type": "", "deprecated": false, @@ -28016,7 +28124,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 423, + "weight": 424, "cookies": false, "type": "", "deprecated": false, @@ -28075,7 +28183,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 421, + "weight": 422, "cookies": false, "type": "", "deprecated": false, @@ -28165,7 +28273,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 422, + "weight": 423, "cookies": false, "type": "", "deprecated": false, @@ -28232,7 +28340,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 424, + "weight": 425, "cookies": false, "type": "", "deprecated": false, @@ -28324,7 +28432,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 425, + "weight": 426, "cookies": false, "type": "", "deprecated": false, @@ -28391,7 +28499,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 206, + "weight": 207, "cookies": false, "type": "", "deprecated": false, @@ -28463,7 +28571,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 205, + "weight": 206, "cookies": false, "type": "", "deprecated": false, @@ -28600,7 +28708,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 207, + "weight": 208, "cookies": false, "type": "", "deprecated": false, @@ -28659,7 +28767,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 208, + "weight": 209, "cookies": false, "type": "", "deprecated": false, @@ -28792,7 +28900,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 209, + "weight": 210, "cookies": false, "type": "", "deprecated": false, @@ -28851,7 +28959,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 211, + "weight": 212, "cookies": false, "type": "", "deprecated": false, @@ -28934,7 +29042,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 210, + "weight": 211, "cookies": false, "type": "upload", "deprecated": false, @@ -29024,7 +29132,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 212, + "weight": 213, "cookies": false, "type": "", "deprecated": false, @@ -29094,7 +29202,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 217, + "weight": 218, "cookies": false, "type": "", "deprecated": false, @@ -29183,7 +29291,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 218, + "weight": 219, "cookies": false, "type": "", "deprecated": false, @@ -29253,7 +29361,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 214, + "weight": 215, "cookies": false, "type": "location", "deprecated": false, @@ -29332,7 +29440,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 213, + "weight": 214, "cookies": false, "type": "location", "deprecated": false, @@ -29538,7 +29646,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 215, + "weight": 216, "cookies": false, "type": "location", "deprecated": false, @@ -29617,7 +29725,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 219, + "weight": 220, "cookies": false, "type": "", "deprecated": false, @@ -29687,7 +29795,7 @@ "x-appwrite": { "method": "getBucketUsage", "group": null, - "weight": 220, + "weight": 221, "cookies": false, "type": "", "deprecated": false, @@ -29765,7 +29873,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 222, + "weight": 223, "cookies": false, "type": "", "deprecated": false, @@ -29840,7 +29948,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 221, + "weight": 222, "cookies": false, "type": "", "deprecated": false, @@ -29930,7 +30038,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 223, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -29992,7 +30100,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 225, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -30067,7 +30175,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 227, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -30129,7 +30237,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 234, + "weight": 235, "cookies": false, "type": "", "deprecated": false, @@ -30199,7 +30307,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 229, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -30282,7 +30390,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 228, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -30395,7 +30503,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 230, + "weight": 231, "cookies": false, "type": "", "deprecated": false, @@ -30465,7 +30573,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 231, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -30551,7 +30659,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 233, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -30623,7 +30731,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 232, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -30716,7 +30824,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 224, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -30776,7 +30884,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 226, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -30854,7 +30962,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 438, + "weight": 439, "cookies": false, "type": "", "deprecated": false, @@ -30922,7 +31030,7 @@ "tags": [ "tokens" ], - "description": "Create a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "description": "Create a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "responses": { "201": { "description": "ResourceToken", @@ -30934,12 +31042,12 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 436, + "weight": 437, "cookies": false, "type": "", "deprecated": false, "demo": "tokens\/create-file-token.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "rate-limit": 60, "rate-time": 60, "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", @@ -31018,7 +31126,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 437, + "weight": 438, "cookies": false, "type": "", "deprecated": false, @@ -31078,7 +31186,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 439, + "weight": 440, "cookies": false, "type": "", "deprecated": false, @@ -31149,7 +31257,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 440, + "weight": 441, "cookies": false, "type": "", "deprecated": false, @@ -31209,7 +31317,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 244, + "weight": 245, "cookies": false, "type": "", "deprecated": false, @@ -31281,7 +31389,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 235, + "weight": 236, "cookies": false, "type": "", "deprecated": false, @@ -31376,7 +31484,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 238, + "weight": 239, "cookies": false, "type": "", "deprecated": false, @@ -31467,7 +31575,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 236, + "weight": 237, "cookies": false, "type": "", "deprecated": false, @@ -31556,7 +31664,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 252, + "weight": 253, "cookies": false, "type": "", "deprecated": false, @@ -31625,7 +31733,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 275, + "weight": 276, "cookies": false, "type": "", "deprecated": false, @@ -31686,7 +31794,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 237, + "weight": 238, "cookies": false, "type": "", "deprecated": false, @@ -31777,7 +31885,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 240, + "weight": 241, "cookies": false, "type": "", "deprecated": false, @@ -31868,7 +31976,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 241, + "weight": 242, "cookies": false, "type": "", "deprecated": false, @@ -31994,7 +32102,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 242, + "weight": 243, "cookies": false, "type": "", "deprecated": false, @@ -32106,7 +32214,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 239, + "weight": 240, "cookies": false, "type": "", "deprecated": false, @@ -32216,7 +32324,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 277, + "weight": 278, "cookies": false, "type": "", "deprecated": false, @@ -32286,7 +32394,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 245, + "weight": 246, "cookies": false, "type": "", "deprecated": false, @@ -32340,7 +32448,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 273, + "weight": 274, "cookies": false, "type": "", "deprecated": false, @@ -32401,7 +32509,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 258, + "weight": 259, "cookies": false, "type": "", "deprecated": false, @@ -32480,7 +32588,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 276, + "weight": 277, "cookies": false, "type": "", "deprecated": false, @@ -32562,7 +32670,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 254, + "weight": 255, "cookies": false, "type": "", "deprecated": false, @@ -32642,7 +32750,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 250, + "weight": 251, "cookies": false, "type": "", "deprecated": false, @@ -32713,7 +32821,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 249, + "weight": 250, "cookies": false, "type": "", "deprecated": false, @@ -32795,7 +32903,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 263, + "weight": 264, "cookies": false, "type": "", "deprecated": false, @@ -32869,7 +32977,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 268, + "weight": 269, "cookies": false, "type": "", "deprecated": false, @@ -32941,7 +33049,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 264, + "weight": 265, "cookies": false, "type": "", "deprecated": false, @@ -33000,7 +33108,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 265, + "weight": 266, "cookies": false, "type": "", "deprecated": false, @@ -33059,7 +33167,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 267, + "weight": 268, "cookies": false, "type": "", "deprecated": false, @@ -33118,7 +33226,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 266, + "weight": 267, "cookies": false, "type": "", "deprecated": false, @@ -33179,7 +33287,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 256, + "weight": 257, "cookies": false, "type": "", "deprecated": false, @@ -33258,7 +33366,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 257, + "weight": 258, "cookies": false, "type": "", "deprecated": false, @@ -33337,7 +33445,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 259, + "weight": 260, "cookies": false, "type": "", "deprecated": false, @@ -33414,7 +33522,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 246, + "weight": 247, "cookies": false, "type": "", "deprecated": false, @@ -33473,7 +33581,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 261, + "weight": 262, "cookies": false, "type": "", "deprecated": false, @@ -33550,7 +33658,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 248, + "weight": 249, "cookies": false, "type": "", "deprecated": false, @@ -33609,7 +33717,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 269, + "weight": 270, "cookies": false, "type": "", "deprecated": false, @@ -33663,7 +33771,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 272, + "weight": 273, "cookies": false, "type": "", "deprecated": false, @@ -33719,7 +33827,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 271, + "weight": 272, "cookies": false, "type": "", "deprecated": false, @@ -33788,7 +33896,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 253, + "weight": 254, "cookies": false, "type": "", "deprecated": false, @@ -33865,7 +33973,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 251, + "weight": 252, "cookies": false, "type": "", "deprecated": false, @@ -33937,7 +34045,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 243, + "weight": 244, "cookies": false, "type": "", "deprecated": false, @@ -34048,7 +34156,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 247, + "weight": 248, "cookies": false, "type": "", "deprecated": false, @@ -34116,7 +34224,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 262, + "weight": 263, "cookies": false, "type": "", "deprecated": false, @@ -34206,7 +34314,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 274, + "weight": 275, "cookies": false, "type": "", "deprecated": false, @@ -34276,7 +34384,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 270, + "weight": 271, "cookies": false, "type": "", "deprecated": false, @@ -34358,7 +34466,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 260, + "weight": 261, "cookies": false, "type": "", "deprecated": false, @@ -34437,7 +34545,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 255, + "weight": 256, "cookies": false, "type": "", "deprecated": false, @@ -34516,7 +34624,7 @@ "x-appwrite": { "method": "createRepositoryDetection", "group": "repositories", - "weight": 281, + "weight": 282, "cookies": false, "type": "", "deprecated": false, @@ -34611,7 +34719,7 @@ "x-appwrite": { "method": "listRepositories", "group": "repositories", - "weight": 282, + "weight": 283, "cookies": false, "type": "", "deprecated": false, @@ -34692,7 +34800,7 @@ "x-appwrite": { "method": "createRepository", "group": "repositories", - "weight": 283, + "weight": 284, "cookies": false, "type": "", "deprecated": false, @@ -34775,7 +34883,7 @@ "x-appwrite": { "method": "getRepository", "group": "repositories", - "weight": 284, + "weight": 285, "cookies": false, "type": "", "deprecated": false, @@ -34841,7 +34949,7 @@ "x-appwrite": { "method": "listRepositoryBranches", "group": "repositories", - "weight": 285, + "weight": 286, "cookies": false, "type": "", "deprecated": false, @@ -34907,7 +35015,7 @@ "x-appwrite": { "method": "getRepositoryContents", "group": "repositories", - "weight": 280, + "weight": 281, "cookies": false, "type": "", "deprecated": false, @@ -34981,7 +35089,7 @@ "x-appwrite": { "method": "updateExternalDeployments", "group": "repositories", - "weight": 290, + "weight": 291, "cookies": false, "type": "", "deprecated": false, @@ -35065,7 +35173,7 @@ "x-appwrite": { "method": "listInstallations", "group": "installations", - "weight": 287, + "weight": 288, "cookies": false, "type": "", "deprecated": false, @@ -35136,7 +35244,7 @@ "x-appwrite": { "method": "getInstallation", "group": "installations", - "weight": 288, + "weight": 289, "cookies": false, "type": "", "deprecated": false, @@ -35189,7 +35297,7 @@ "x-appwrite": { "method": "deleteInstallation", "group": "installations", - "weight": 289, + "weight": 290, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/swagger2-1.7.x-server.json b/app/config/specs/swagger2-1.7.x-server.json index d2924041c0..083290bcc0 100644 --- a/app/config/specs/swagger2-1.7.x-server.json +++ b/app/config/specs/swagger2-1.7.x-server.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "version": "1.7.0", + "version": "1.7.4", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -7769,7 +7769,7 @@ "x-appwrite": { "method": "upsertDocuments", "group": "documents", - "weight": 115, + "weight": 116, "cookies": false, "type": "", "deprecated": false, @@ -7820,13 +7820,16 @@ "documents": { "type": "array", "description": "Array of document data as JSON objects. May contain partial documents.", - "default": [], + "default": null, "x-example": null, "items": { "type": "object" } } - } + }, + "required": [ + "documents" + ] } } ] @@ -7855,7 +7858,7 @@ "x-appwrite": { "method": "updateDocuments", "group": "documents", - "weight": 114, + "weight": 115, "cookies": false, "type": "", "deprecated": false, @@ -7947,7 +7950,7 @@ "x-appwrite": { "method": "deleteDocuments", "group": "documents", - "weight": 117, + "weight": 118, "cookies": false, "type": "", "deprecated": false, @@ -8101,6 +8104,113 @@ } ] }, + "put": { + "summary": "Upsert document", + "operationId": "databasesUpsertDocument", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "responses": { + "200": { + "description": "Document", + "schema": { + "$ref": "#\/definitions\/document" + } + } + }, + "x-appwrite": { + "method": "upsertDocument", + "group": "documents", + "weight": 114, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/upsert-document.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/upsert-document.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "client", + "server", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Session": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Document data as JSON object. Include all required attributes of the document to be created or updated.", + "default": {}, + "x-example": "{}" + }, + "permissions": { + "type": "array", + "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", + "default": null, + "x-example": "[\"read(\"any\")\"]", + "items": { + "type": "string" + } + } + }, + "required": [ + "data" + ] + } + } + ] + }, "patch": { "summary": "Update document", "operationId": "databasesUpdateDocument", @@ -8224,7 +8334,7 @@ "x-appwrite": { "method": "deleteDocument", "group": "documents", - "weight": 116, + "weight": 117, "cookies": false, "type": "", "deprecated": false, @@ -8657,7 +8767,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 375, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -8730,7 +8840,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 372, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -8980,7 +9090,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 377, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -9030,7 +9140,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 378, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -9081,7 +9191,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 373, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -9141,7 +9251,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 374, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -9387,7 +9497,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 376, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -9449,7 +9559,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 381, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -9527,7 +9637,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 382, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -9608,7 +9718,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 379, + "weight": 380, "cookies": false, "type": "upload", "deprecated": false, @@ -9701,7 +9811,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 387, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -9787,7 +9897,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 384, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -9894,7 +10004,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 385, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -9991,7 +10101,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 380, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -10054,7 +10164,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 383, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -10122,7 +10232,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 386, + "weight": 387, "cookies": false, "type": "location", "deprecated": false, @@ -10209,7 +10319,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 388, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -10277,7 +10387,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 391, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -10353,7 +10463,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 389, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -10472,7 +10582,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 390, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -10539,7 +10649,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 392, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -10607,7 +10717,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 397, + "weight": 398, "cookies": false, "type": "", "deprecated": false, @@ -10667,7 +10777,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 395, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -10758,7 +10868,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 396, + "weight": 397, "cookies": false, "type": "", "deprecated": false, @@ -10826,7 +10936,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 398, + "weight": 399, "cookies": false, "type": "", "deprecated": false, @@ -10919,7 +11029,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 399, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -10989,7 +11099,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 305, + "weight": 306, "cookies": false, "type": "graphql", "deprecated": false, @@ -11065,7 +11175,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 304, + "weight": 305, "cookies": false, "type": "graphql", "deprecated": false, @@ -11139,7 +11249,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 129, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -11189,7 +11299,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 150, + "weight": 151, "cookies": false, "type": "", "deprecated": false, @@ -11239,7 +11349,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 132, + "weight": 133, "cookies": false, "type": "", "deprecated": false, @@ -11289,7 +11399,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 137, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -11348,7 +11458,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 131, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -11398,7 +11508,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 133, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -11448,7 +11558,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 139, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -11509,7 +11619,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 138, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -11570,7 +11680,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 140, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -11640,7 +11750,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 141, + "weight": 142, "cookies": false, "type": "", "deprecated": false, @@ -11701,7 +11811,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 151, + "weight": 152, "cookies": false, "type": "", "deprecated": false, @@ -11786,7 +11896,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 145, + "weight": 146, "cookies": false, "type": "", "deprecated": false, @@ -11847,7 +11957,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 136, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -11908,7 +12018,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 142, + "weight": 143, "cookies": false, "type": "", "deprecated": false, @@ -11969,7 +12079,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 143, + "weight": 144, "cookies": false, "type": "", "deprecated": false, @@ -12030,7 +12140,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 144, + "weight": 145, "cookies": false, "type": "", "deprecated": false, @@ -12091,7 +12201,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 146, + "weight": 147, "cookies": false, "type": "", "deprecated": false, @@ -12152,7 +12262,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 147, + "weight": 148, "cookies": false, "type": "", "deprecated": false, @@ -12213,7 +12323,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 135, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -12274,7 +12384,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 149, + "weight": 150, "cookies": false, "type": "", "deprecated": false, @@ -12324,7 +12434,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 148, + "weight": 149, "cookies": false, "type": "", "deprecated": false, @@ -12374,7 +12484,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 134, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -12424,7 +12534,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 121, + "weight": 122, "cookies": false, "type": "", "deprecated": false, @@ -12478,7 +12588,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 122, + "weight": 123, "cookies": false, "type": "", "deprecated": false, @@ -12532,7 +12642,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 126, + "weight": 127, "cookies": false, "type": "", "deprecated": false, @@ -12586,7 +12696,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 123, + "weight": 124, "cookies": false, "type": "", "deprecated": false, @@ -12640,7 +12750,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 124, + "weight": 125, "cookies": false, "type": "", "deprecated": false, @@ -12694,7 +12804,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 125, + "weight": 126, "cookies": false, "type": "", "deprecated": false, @@ -12748,7 +12858,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 127, + "weight": 128, "cookies": false, "type": "", "deprecated": false, @@ -12802,7 +12912,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 128, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -12856,7 +12966,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 359, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -12932,7 +13042,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 356, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -13091,7 +13201,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 363, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -13247,7 +13357,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 358, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -13443,7 +13553,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 365, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -13638,7 +13748,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 357, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -13757,7 +13867,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 364, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -13872,7 +13982,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 362, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -13928,7 +14038,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 366, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -13989,7 +14099,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 360, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -14062,7 +14172,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 361, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -14135,7 +14245,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 331, + "weight": 332, "cookies": false, "type": "", "deprecated": false, @@ -14211,7 +14321,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 330, + "weight": 331, "cookies": false, "type": "", "deprecated": false, @@ -14327,7 +14437,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 343, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -14441,7 +14551,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 329, + "weight": 330, "cookies": false, "type": "", "deprecated": false, @@ -14533,7 +14643,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 342, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -14623,7 +14733,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 321, + "weight": 322, "cookies": false, "type": "", "deprecated": false, @@ -14751,7 +14861,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 334, + "weight": 335, "cookies": false, "type": "", "deprecated": false, @@ -14877,7 +14987,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 324, + "weight": 325, "cookies": false, "type": "", "deprecated": false, @@ -14981,7 +15091,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 337, + "weight": 338, "cookies": false, "type": "", "deprecated": false, @@ -15083,7 +15193,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 322, + "weight": 323, "cookies": false, "type": "", "deprecated": false, @@ -15199,7 +15309,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 335, + "weight": 336, "cookies": false, "type": "", "deprecated": false, @@ -15313,7 +15423,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 323, + "weight": 324, "cookies": false, "type": "", "deprecated": false, @@ -15473,7 +15583,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 336, + "weight": 337, "cookies": false, "type": "", "deprecated": false, @@ -15630,7 +15740,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 325, + "weight": 326, "cookies": false, "type": "", "deprecated": false, @@ -15734,7 +15844,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 338, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -15836,7 +15946,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 326, + "weight": 327, "cookies": false, "type": "", "deprecated": false, @@ -15940,7 +16050,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 339, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -16042,7 +16152,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 327, + "weight": 328, "cookies": false, "type": "", "deprecated": false, @@ -16146,7 +16256,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 340, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -16248,7 +16358,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 328, + "weight": 329, "cookies": false, "type": "", "deprecated": false, @@ -16352,7 +16462,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 341, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -16452,7 +16562,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 333, + "weight": 334, "cookies": false, "type": "", "deprecated": false, @@ -16508,7 +16618,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 344, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -16569,7 +16679,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 332, + "weight": 333, "cookies": false, "type": "", "deprecated": false, @@ -16642,7 +16752,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 353, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -16715,7 +16825,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 346, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -16789,7 +16899,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 345, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -16878,7 +16988,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 348, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -16939,7 +17049,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 349, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -17019,7 +17129,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 350, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -17080,7 +17190,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 347, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -17153,7 +17263,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 352, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -17235,7 +17345,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 351, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -17325,7 +17435,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 354, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -17389,7 +17499,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 355, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -17462,7 +17572,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 404, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -17535,7 +17645,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 402, + "weight": 403, "cookies": false, "type": "", "deprecated": false, @@ -17801,7 +17911,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 407, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -17851,7 +17961,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 430, + "weight": 431, "cookies": false, "type": "", "deprecated": false, @@ -17902,7 +18012,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 403, + "weight": 404, "cookies": false, "type": "", "deprecated": false, @@ -17962,7 +18072,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 405, + "weight": 406, "cookies": false, "type": "", "deprecated": false, @@ -18223,7 +18333,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 406, + "weight": 407, "cookies": false, "type": "", "deprecated": false, @@ -18285,7 +18395,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 413, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -18363,7 +18473,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 412, + "weight": 413, "cookies": false, "type": "", "deprecated": false, @@ -18444,7 +18554,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 408, + "weight": 409, "cookies": false, "type": "upload", "deprecated": false, @@ -18545,7 +18655,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 416, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -18625,7 +18735,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 409, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -18732,7 +18842,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 410, + "weight": 411, "cookies": false, "type": "", "deprecated": false, @@ -18830,7 +18940,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 411, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -18893,7 +19003,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 414, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -18961,7 +19071,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 415, + "weight": 416, "cookies": false, "type": "location", "deprecated": false, @@ -19048,7 +19158,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 417, + "weight": 418, "cookies": false, "type": "", "deprecated": false, @@ -19116,7 +19226,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 419, + "weight": 420, "cookies": false, "type": "", "deprecated": false, @@ -19188,7 +19298,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 418, + "weight": 419, "cookies": false, "type": "", "deprecated": false, @@ -19253,7 +19363,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 420, + "weight": 421, "cookies": false, "type": "", "deprecated": false, @@ -19321,7 +19431,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 423, + "weight": 424, "cookies": false, "type": "", "deprecated": false, @@ -19381,7 +19491,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 421, + "weight": 422, "cookies": false, "type": "", "deprecated": false, @@ -19472,7 +19582,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 422, + "weight": 423, "cookies": false, "type": "", "deprecated": false, @@ -19540,7 +19650,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 424, + "weight": 425, "cookies": false, "type": "", "deprecated": false, @@ -19633,7 +19743,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 425, + "weight": 426, "cookies": false, "type": "", "deprecated": false, @@ -19701,7 +19811,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 206, + "weight": 207, "cookies": false, "type": "", "deprecated": false, @@ -19774,7 +19884,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 205, + "weight": 206, "cookies": false, "type": "", "deprecated": false, @@ -19912,7 +20022,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 207, + "weight": 208, "cookies": false, "type": "", "deprecated": false, @@ -19972,7 +20082,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 208, + "weight": 209, "cookies": false, "type": "", "deprecated": false, @@ -20106,7 +20216,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 209, + "weight": 210, "cookies": false, "type": "", "deprecated": false, @@ -20166,7 +20276,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 211, + "weight": 212, "cookies": false, "type": "", "deprecated": false, @@ -20251,7 +20361,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 210, + "weight": 211, "cookies": false, "type": "upload", "deprecated": false, @@ -20343,7 +20453,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 212, + "weight": 213, "cookies": false, "type": "", "deprecated": false, @@ -20415,7 +20525,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 217, + "weight": 218, "cookies": false, "type": "", "deprecated": false, @@ -20506,7 +20616,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 218, + "weight": 219, "cookies": false, "type": "", "deprecated": false, @@ -20578,7 +20688,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 214, + "weight": 215, "cookies": false, "type": "location", "deprecated": false, @@ -20659,7 +20769,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 213, + "weight": 214, "cookies": false, "type": "location", "deprecated": false, @@ -20867,7 +20977,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 215, + "weight": 216, "cookies": false, "type": "location", "deprecated": false, @@ -20948,7 +21058,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 222, + "weight": 223, "cookies": false, "type": "", "deprecated": false, @@ -21025,7 +21135,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 221, + "weight": 222, "cookies": false, "type": "", "deprecated": false, @@ -21117,7 +21227,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 223, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -21181,7 +21291,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 225, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -21258,7 +21368,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 227, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -21322,7 +21432,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 229, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -21407,7 +21517,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 228, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -21522,7 +21632,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 230, + "weight": 231, "cookies": false, "type": "", "deprecated": false, @@ -21594,7 +21704,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 231, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -21682,7 +21792,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 233, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -21756,7 +21866,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 232, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -21851,7 +21961,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 224, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -21913,7 +22023,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 226, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -21993,7 +22103,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 438, + "weight": 439, "cookies": false, "type": "", "deprecated": false, @@ -22062,7 +22172,7 @@ "tags": [ "tokens" ], - "description": "Create a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "description": "Create a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "responses": { "201": { "description": "ResourceToken", @@ -22074,12 +22184,12 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 436, + "weight": 437, "cookies": false, "type": "", "deprecated": false, "demo": "tokens\/create-file-token.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "rate-limit": 60, "rate-time": 60, "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", @@ -22159,7 +22269,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 437, + "weight": 438, "cookies": false, "type": "", "deprecated": false, @@ -22220,7 +22330,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 439, + "weight": 440, "cookies": false, "type": "", "deprecated": false, @@ -22292,7 +22402,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 440, + "weight": 441, "cookies": false, "type": "", "deprecated": false, @@ -22353,7 +22463,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 244, + "weight": 245, "cookies": false, "type": "", "deprecated": false, @@ -22426,7 +22536,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 235, + "weight": 236, "cookies": false, "type": "", "deprecated": false, @@ -22522,7 +22632,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 238, + "weight": 239, "cookies": false, "type": "", "deprecated": false, @@ -22614,7 +22724,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 236, + "weight": 237, "cookies": false, "type": "", "deprecated": false, @@ -22704,7 +22814,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 252, + "weight": 253, "cookies": false, "type": "", "deprecated": false, @@ -22774,7 +22884,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 275, + "weight": 276, "cookies": false, "type": "", "deprecated": false, @@ -22836,7 +22946,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 237, + "weight": 238, "cookies": false, "type": "", "deprecated": false, @@ -22928,7 +23038,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 240, + "weight": 241, "cookies": false, "type": "", "deprecated": false, @@ -23020,7 +23130,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 241, + "weight": 242, "cookies": false, "type": "", "deprecated": false, @@ -23147,7 +23257,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 242, + "weight": 243, "cookies": false, "type": "", "deprecated": false, @@ -23260,7 +23370,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 239, + "weight": 240, "cookies": false, "type": "", "deprecated": false, @@ -23371,7 +23481,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 245, + "weight": 246, "cookies": false, "type": "", "deprecated": false, @@ -23426,7 +23536,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 273, + "weight": 274, "cookies": false, "type": "", "deprecated": false, @@ -23488,7 +23598,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 258, + "weight": 259, "cookies": false, "type": "", "deprecated": false, @@ -23568,7 +23678,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 276, + "weight": 277, "cookies": false, "type": "", "deprecated": false, @@ -23651,7 +23761,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 254, + "weight": 255, "cookies": false, "type": "", "deprecated": false, @@ -23732,7 +23842,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 250, + "weight": 251, "cookies": false, "type": "", "deprecated": false, @@ -23804,7 +23914,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 249, + "weight": 250, "cookies": false, "type": "", "deprecated": false, @@ -23887,7 +23997,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 263, + "weight": 264, "cookies": false, "type": "", "deprecated": false, @@ -23962,7 +24072,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 268, + "weight": 269, "cookies": false, "type": "", "deprecated": false, @@ -24035,7 +24145,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 264, + "weight": 265, "cookies": false, "type": "", "deprecated": false, @@ -24095,7 +24205,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 265, + "weight": 266, "cookies": false, "type": "", "deprecated": false, @@ -24155,7 +24265,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 267, + "weight": 268, "cookies": false, "type": "", "deprecated": false, @@ -24215,7 +24325,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 266, + "weight": 267, "cookies": false, "type": "", "deprecated": false, @@ -24277,7 +24387,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 256, + "weight": 257, "cookies": false, "type": "", "deprecated": false, @@ -24357,7 +24467,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 257, + "weight": 258, "cookies": false, "type": "", "deprecated": false, @@ -24437,7 +24547,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 259, + "weight": 260, "cookies": false, "type": "", "deprecated": false, @@ -24515,7 +24625,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 246, + "weight": 247, "cookies": false, "type": "", "deprecated": false, @@ -24575,7 +24685,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 261, + "weight": 262, "cookies": false, "type": "", "deprecated": false, @@ -24653,7 +24763,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 248, + "weight": 249, "cookies": false, "type": "", "deprecated": false, @@ -24713,7 +24823,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 269, + "weight": 270, "cookies": false, "type": "", "deprecated": false, @@ -24768,7 +24878,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 272, + "weight": 273, "cookies": false, "type": "", "deprecated": false, @@ -24825,7 +24935,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 271, + "weight": 272, "cookies": false, "type": "", "deprecated": false, @@ -24895,7 +25005,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 253, + "weight": 254, "cookies": false, "type": "", "deprecated": false, @@ -24973,7 +25083,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 251, + "weight": 252, "cookies": false, "type": "", "deprecated": false, @@ -25046,7 +25156,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 243, + "weight": 244, "cookies": false, "type": "", "deprecated": false, @@ -25158,7 +25268,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 247, + "weight": 248, "cookies": false, "type": "", "deprecated": false, @@ -25227,7 +25337,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 262, + "weight": 263, "cookies": false, "type": "", "deprecated": false, @@ -25318,7 +25428,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 274, + "weight": 275, "cookies": false, "type": "", "deprecated": false, @@ -25389,7 +25499,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 270, + "weight": 271, "cookies": false, "type": "", "deprecated": false, @@ -25472,7 +25582,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 260, + "weight": 261, "cookies": false, "type": "", "deprecated": false, @@ -25552,7 +25662,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 255, + "weight": 256, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 9bfe261df1..33fe1a93c9 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "version": "1.7.0", + "version": "1.7.4", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -61,6 +61,12 @@ "name": "X-Appwrite-Session", "description": "The user session to authenticate with", "in": "header" + }, + "DevKey": { + "type": "apiKey", + "name": "X-Appwrite-Dev-Key", + "description": "Your secret dev API key", + "in": "header" } }, "paths": { @@ -4597,6 +4603,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "client", "server", "server" @@ -4606,6 +4613,7 @@ { "name": "createDocument", "auth": { + "Admin": [], "Session": [], "Key": [], "JWT": [] @@ -4791,6 +4799,111 @@ } ] }, + "put": { + "summary": "Upsert document", + "operationId": "databasesUpsertDocument", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "responses": { + "200": { + "description": "Document", + "schema": { + "$ref": "#\/definitions\/document" + } + } + }, + "x-appwrite": { + "method": "upsertDocument", + "group": "documents", + "weight": 114, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/upsert-document.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/upsert-document.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "client", + "server", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Document data as JSON object. Include all required attributes of the document to be created or updated.", + "default": {}, + "x-example": "{}" + }, + "permissions": { + "type": "array", + "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", + "default": null, + "x-example": "[\"read(\"any\")\"]", + "items": { + "type": "string" + } + } + }, + "required": [ + "data" + ] + } + } + ] + }, "patch": { "summary": "Update document", "operationId": "databasesUpdateDocument", @@ -4912,7 +5025,7 @@ "x-appwrite": { "method": "deleteDocument", "group": "documents", - "weight": 116, + "weight": 119, "cookies": false, "type": "", "deprecated": false, @@ -4967,6 +5080,224 @@ ] } }, + "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/decrement": { + "patch": { + "summary": "Decrement document attribute", + "operationId": "databasesDecrementDocumentAttribute", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Decrement a specific attribute of a document by a given value.", + "responses": { + "200": { + "description": "Document", + "schema": { + "$ref": "#\/definitions\/document" + } + } + }, + "x-appwrite": { + "method": "decrementDocumentAttribute", + "group": "documents", + "weight": 116, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/decrement-document-attribute.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/decrement-document-attribute.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "console", + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "attribute", + "description": "Attribute key.", + "required": true, + "type": "string", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "value": { + "type": "number", + "description": "Value to decrement the attribute by. The value must be a number.", + "default": 1, + "x-example": null + }, + "min": { + "type": "number", + "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", + "default": null, + "x-example": null + } + } + } + } + ] + } + }, + "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/increment": { + "patch": { + "summary": "Increment document attribute", + "operationId": "databasesIncrementDocumentAttribute", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Increment a specific attribute of a document by a given value.", + "responses": { + "200": { + "description": "Document", + "schema": { + "$ref": "#\/definitions\/document" + } + } + }, + "x-appwrite": { + "method": "incrementDocumentAttribute", + "group": "documents", + "weight": 115, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/increment-document-attribute.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/increment-document-attribute.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "console", + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "attribute", + "description": "Attribute key.", + "required": true, + "type": "string", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "value": { + "type": "number", + "description": "Value to increment the attribute by. The value must be a number.", + "default": 1, + "x-example": null + }, + "max": { + "type": "number", + "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", + "default": null, + "x-example": null + } + } + } + } + ] + } + }, "\/functions\/{functionId}\/executions": { "get": { "summary": "List executions", @@ -4990,7 +5321,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 391, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -5064,7 +5395,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 389, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -5181,7 +5512,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 390, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -5253,7 +5584,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 305, + "weight": 308, "cookies": false, "type": "graphql", "deprecated": false, @@ -5327,7 +5658,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 304, + "weight": 307, "cookies": false, "type": "graphql", "deprecated": false, @@ -5399,7 +5730,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 121, + "weight": 124, "cookies": false, "type": "", "deprecated": false, @@ -5451,7 +5782,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 122, + "weight": 125, "cookies": false, "type": "", "deprecated": false, @@ -5503,7 +5834,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 126, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -5555,7 +5886,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 123, + "weight": 126, "cookies": false, "type": "", "deprecated": false, @@ -5607,7 +5938,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 124, + "weight": 127, "cookies": false, "type": "", "deprecated": false, @@ -5659,7 +5990,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 125, + "weight": 128, "cookies": false, "type": "", "deprecated": false, @@ -5711,7 +6042,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 127, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -5763,7 +6094,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 128, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -5817,7 +6148,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 351, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -5902,7 +6233,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 355, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -5973,7 +6304,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 211, + "weight": 214, "cookies": false, "type": "", "deprecated": false, @@ -6056,7 +6387,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 210, + "weight": 213, "cookies": false, "type": "upload", "deprecated": false, @@ -6146,7 +6477,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 212, + "weight": 215, "cookies": false, "type": "", "deprecated": false, @@ -6216,7 +6547,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 217, + "weight": 220, "cookies": false, "type": "", "deprecated": false, @@ -6305,7 +6636,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 218, + "weight": 221, "cookies": false, "type": "", "deprecated": false, @@ -6375,7 +6706,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 214, + "weight": 217, "cookies": false, "type": "location", "deprecated": false, @@ -6454,7 +6785,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 213, + "weight": 216, "cookies": false, "type": "location", "deprecated": false, @@ -6618,7 +6949,8 @@ "png", "webp", "heic", - "avif" + "avif", + "gif" ], "x-enum-name": "ImageFormat", "x-enum-keys": [], @@ -6660,7 +6992,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 215, + "weight": 218, "cookies": false, "type": "location", "deprecated": false, @@ -6739,7 +7071,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 222, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -6814,7 +7146,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 221, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -6904,7 +7236,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 223, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -6966,7 +7298,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 225, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -7041,7 +7373,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 227, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -7103,7 +7435,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 229, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -7186,7 +7518,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 228, + "weight": 231, "cookies": false, "type": "", "deprecated": false, @@ -7299,7 +7631,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 230, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -7369,7 +7701,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 231, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -7455,7 +7787,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 233, + "weight": 236, "cookies": false, "type": "", "deprecated": false, @@ -7527,7 +7859,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 232, + "weight": 235, "cookies": false, "type": "", "deprecated": false, @@ -7621,7 +7953,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 224, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -7682,7 +8014,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 226, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -8174,6 +8506,12 @@ "description": "Document ID.", "x-example": "5e5ea5c16897e" }, + "$sequence": { + "type": "integer", + "description": "Document automatically incrementing ID.", + "x-example": 1, + "format": "int32" + }, "$collectionId": { "type": "string", "description": "Collection ID.", @@ -8208,6 +8546,7 @@ "additionalProperties": true, "required": [ "$id", + "$sequence", "$collectionId", "$databaseId", "$createdAt", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index d7f8a4c9d1..a11fd21b42 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "version": "1.7.0", + "version": "1.7.4", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -4527,7 +4527,7 @@ "x-appwrite": { "method": "chat", "group": "console", - "weight": 307, + "weight": 310, "cookies": false, "type": "", "deprecated": false, @@ -4590,7 +4590,7 @@ "x-appwrite": { "method": "getResource", "group": null, - "weight": 431, + "weight": 434, "cookies": false, "type": "", "deprecated": false, @@ -4661,7 +4661,7 @@ "x-appwrite": { "method": "variables", "group": "console", - "weight": 306, + "weight": 309, "cookies": false, "type": "", "deprecated": false, @@ -4863,7 +4863,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 118, + "weight": 121, "cookies": false, "type": "", "deprecated": false, @@ -8145,6 +8145,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "client", "server", "server" @@ -8154,6 +8155,7 @@ { "name": "createDocument", "auth": { + "Admin": [], "Session": [], "Key": [], "JWT": [] @@ -8182,6 +8184,7 @@ { "name": "createDocuments", "auth": { + "Admin": [], "Key": [] }, "parameters": [ @@ -8296,7 +8299,7 @@ "x-appwrite": { "method": "upsertDocuments", "group": "documents", - "weight": 115, + "weight": 118, "cookies": false, "type": "", "deprecated": false, @@ -8307,6 +8310,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "server" ], "packaging": false, @@ -8346,13 +8350,16 @@ "documents": { "type": "array", "description": "Array of document data as JSON objects. May contain partial documents.", - "default": [], + "default": null, "x-example": null, "items": { "type": "object" } } - } + }, + "required": [ + "documents" + ] } } ] @@ -8381,7 +8388,7 @@ "x-appwrite": { "method": "updateDocuments", "group": "documents", - "weight": 114, + "weight": 117, "cookies": false, "type": "", "deprecated": false, @@ -8392,6 +8399,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "server" ], "packaging": false, @@ -8472,7 +8480,7 @@ "x-appwrite": { "method": "deleteDocuments", "group": "documents", - "weight": 117, + "weight": 120, "cookies": false, "type": "", "deprecated": false, @@ -8483,6 +8491,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "server" ], "packaging": false, @@ -8623,6 +8632,111 @@ } ] }, + "put": { + "summary": "Upsert document", + "operationId": "databasesUpsertDocument", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "responses": { + "200": { + "description": "Document", + "schema": { + "$ref": "#\/definitions\/document" + } + } + }, + "x-appwrite": { + "method": "upsertDocument", + "group": "documents", + "weight": 114, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/upsert-document.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/upsert-document.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "client", + "server", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Document data as JSON object. Include all required attributes of the document to be created or updated.", + "default": {}, + "x-example": "{}" + }, + "permissions": { + "type": "array", + "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", + "default": null, + "x-example": "[\"read(\"any\")\"]", + "items": { + "type": "string" + } + } + }, + "required": [ + "data" + ] + } + } + ] + }, "patch": { "summary": "Update document", "operationId": "databasesUpdateDocument", @@ -8744,7 +8858,7 @@ "x-appwrite": { "method": "deleteDocument", "group": "documents", - "weight": 116, + "weight": 119, "cookies": false, "type": "", "deprecated": false, @@ -8885,6 +8999,224 @@ ] } }, + "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/decrement": { + "patch": { + "summary": "Decrement document attribute", + "operationId": "databasesDecrementDocumentAttribute", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Decrement a specific attribute of a document by a given value.", + "responses": { + "200": { + "description": "Document", + "schema": { + "$ref": "#\/definitions\/document" + } + } + }, + "x-appwrite": { + "method": "decrementDocumentAttribute", + "group": "documents", + "weight": 116, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/decrement-document-attribute.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/decrement-document-attribute.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "console", + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "attribute", + "description": "Attribute key.", + "required": true, + "type": "string", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "value": { + "type": "number", + "description": "Value to decrement the attribute by. The value must be a number.", + "default": 1, + "x-example": null + }, + "min": { + "type": "number", + "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", + "default": null, + "x-example": null + } + } + } + } + ] + } + }, + "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/increment": { + "patch": { + "summary": "Increment document attribute", + "operationId": "databasesIncrementDocumentAttribute", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Increment a specific attribute of a document by a given value.", + "responses": { + "200": { + "description": "Document", + "schema": { + "$ref": "#\/definitions\/document" + } + } + }, + "x-appwrite": { + "method": "incrementDocumentAttribute", + "group": "documents", + "weight": 115, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/increment-document-attribute.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/increment-document-attribute.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "console", + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "attribute", + "description": "Attribute key.", + "required": true, + "type": "string", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "value": { + "type": "number", + "description": "Value to increment the attribute by. The value must be a number.", + "default": 1, + "x-example": null + }, + "max": { + "type": "number", + "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", + "default": null, + "x-example": null + } + } + } + } + ] + } + }, "\/databases\/{databaseId}\/collections\/{collectionId}\/indexes": { "get": { "summary": "List indexes", @@ -9335,7 +9667,7 @@ "x-appwrite": { "method": "getCollectionUsage", "group": null, - "weight": 120, + "weight": 123, "cookies": false, "type": "", "deprecated": false, @@ -9491,7 +9823,7 @@ "x-appwrite": { "method": "getDatabaseUsage", "group": null, - "weight": 119, + "weight": 122, "cookies": false, "type": "", "deprecated": false, @@ -9569,7 +9901,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 375, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -9641,7 +9973,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 372, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -9727,6 +10059,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -9752,7 +10085,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -9890,7 +10224,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 377, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -9939,7 +10273,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 378, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -9989,7 +10323,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 401, + "weight": 404, "cookies": false, "type": "", "deprecated": false, @@ -10083,7 +10417,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 400, + "weight": 403, "cookies": false, "type": "", "deprecated": false, @@ -10141,7 +10475,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 394, + "weight": 397, "cookies": false, "type": "", "deprecated": false, @@ -10211,7 +10545,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 373, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -10270,7 +10604,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 374, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -10358,6 +10692,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -10383,7 +10718,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -10515,7 +10851,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 376, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -10576,7 +10912,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 381, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -10653,7 +10989,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 382, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -10733,7 +11069,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 379, + "weight": 382, "cookies": false, "type": "upload", "deprecated": false, @@ -10825,7 +11161,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 387, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -10910,7 +11246,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 384, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -11016,7 +11352,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 385, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -11112,7 +11448,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 380, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -11174,7 +11510,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 383, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -11241,7 +11577,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 386, + "weight": 389, "cookies": false, "type": "location", "deprecated": false, @@ -11327,7 +11663,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 388, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -11394,7 +11730,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 391, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -11468,7 +11804,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 389, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -11585,7 +11921,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 390, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -11650,7 +11986,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 392, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -11717,7 +12053,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 393, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -11795,7 +12131,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 397, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -11854,7 +12190,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 395, + "weight": 398, "cookies": false, "type": "", "deprecated": false, @@ -11944,7 +12280,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 396, + "weight": 399, "cookies": false, "type": "", "deprecated": false, @@ -12011,7 +12347,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 398, + "weight": 401, "cookies": false, "type": "", "deprecated": false, @@ -12103,7 +12439,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 399, + "weight": 402, "cookies": false, "type": "", "deprecated": false, @@ -12172,7 +12508,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 305, + "weight": 308, "cookies": false, "type": "graphql", "deprecated": false, @@ -12246,7 +12582,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 304, + "weight": 307, "cookies": false, "type": "graphql", "deprecated": false, @@ -12318,7 +12654,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 129, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -12367,7 +12703,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 150, + "weight": 153, "cookies": false, "type": "", "deprecated": false, @@ -12416,7 +12752,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 132, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -12465,7 +12801,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 137, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -12523,7 +12859,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 131, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -12572,7 +12908,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 133, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -12621,7 +12957,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 139, + "weight": 142, "cookies": false, "type": "", "deprecated": false, @@ -12681,7 +13017,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 138, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -12741,7 +13077,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 140, + "weight": 143, "cookies": false, "type": "", "deprecated": false, @@ -12810,7 +13146,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 141, + "weight": 144, "cookies": false, "type": "", "deprecated": false, @@ -12870,7 +13206,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 151, + "weight": 154, "cookies": false, "type": "", "deprecated": false, @@ -12954,7 +13290,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 145, + "weight": 148, "cookies": false, "type": "", "deprecated": false, @@ -13014,7 +13350,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 136, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -13074,7 +13410,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 142, + "weight": 145, "cookies": false, "type": "", "deprecated": false, @@ -13134,7 +13470,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 143, + "weight": 146, "cookies": false, "type": "", "deprecated": false, @@ -13194,7 +13530,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 144, + "weight": 147, "cookies": false, "type": "", "deprecated": false, @@ -13254,7 +13590,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 146, + "weight": 149, "cookies": false, "type": "", "deprecated": false, @@ -13314,7 +13650,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 147, + "weight": 150, "cookies": false, "type": "", "deprecated": false, @@ -13374,7 +13710,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 135, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -13434,7 +13770,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 149, + "weight": 152, "cookies": false, "type": "", "deprecated": false, @@ -13483,7 +13819,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 148, + "weight": 151, "cookies": false, "type": "", "deprecated": false, @@ -13532,7 +13868,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 134, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -13581,7 +13917,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 121, + "weight": 124, "cookies": false, "type": "", "deprecated": false, @@ -13633,7 +13969,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 122, + "weight": 125, "cookies": false, "type": "", "deprecated": false, @@ -13685,7 +14021,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 126, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -13737,7 +14073,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 123, + "weight": 126, "cookies": false, "type": "", "deprecated": false, @@ -13789,7 +14125,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 124, + "weight": 127, "cookies": false, "type": "", "deprecated": false, @@ -13841,7 +14177,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 125, + "weight": 128, "cookies": false, "type": "", "deprecated": false, @@ -13893,7 +14229,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 127, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -13945,7 +14281,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 128, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -13997,7 +14333,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 359, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -14072,7 +14408,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 356, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -14230,7 +14566,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 363, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -14385,7 +14721,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 358, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -14580,7 +14916,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 365, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -14774,7 +15110,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 357, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -14892,7 +15228,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 364, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -15006,7 +15342,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 362, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -15061,7 +15397,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 366, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -15121,7 +15457,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 360, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -15193,7 +15529,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 361, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -15265,7 +15601,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 331, + "weight": 334, "cookies": false, "type": "", "deprecated": false, @@ -15340,7 +15676,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 330, + "weight": 333, "cookies": false, "type": "", "deprecated": false, @@ -15455,7 +15791,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 343, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -15568,7 +15904,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 329, + "weight": 332, "cookies": false, "type": "", "deprecated": false, @@ -15659,7 +15995,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 342, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -15748,7 +16084,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 321, + "weight": 324, "cookies": false, "type": "", "deprecated": false, @@ -15875,7 +16211,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 334, + "weight": 337, "cookies": false, "type": "", "deprecated": false, @@ -16000,7 +16336,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 324, + "weight": 327, "cookies": false, "type": "", "deprecated": false, @@ -16103,7 +16439,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 337, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -16204,7 +16540,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 322, + "weight": 325, "cookies": false, "type": "", "deprecated": false, @@ -16319,7 +16655,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 335, + "weight": 338, "cookies": false, "type": "", "deprecated": false, @@ -16432,7 +16768,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 323, + "weight": 326, "cookies": false, "type": "", "deprecated": false, @@ -16591,7 +16927,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 336, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -16747,7 +17083,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 325, + "weight": 328, "cookies": false, "type": "", "deprecated": false, @@ -16850,7 +17186,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 338, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -16951,7 +17287,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 326, + "weight": 329, "cookies": false, "type": "", "deprecated": false, @@ -17054,7 +17390,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 339, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -17155,7 +17491,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 327, + "weight": 330, "cookies": false, "type": "", "deprecated": false, @@ -17258,7 +17594,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 340, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -17359,7 +17695,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 328, + "weight": 331, "cookies": false, "type": "", "deprecated": false, @@ -17462,7 +17798,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 341, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -17561,7 +17897,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 333, + "weight": 336, "cookies": false, "type": "", "deprecated": false, @@ -17616,7 +17952,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 344, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -17676,7 +18012,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 332, + "weight": 335, "cookies": false, "type": "", "deprecated": false, @@ -17748,7 +18084,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 353, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -17820,7 +18156,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 346, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -17893,7 +18229,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 345, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -17981,7 +18317,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 348, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -18041,7 +18377,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 349, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -18120,7 +18456,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 350, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -18180,7 +18516,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 347, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -18252,7 +18588,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 352, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -18333,7 +18669,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 351, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -18421,7 +18757,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 354, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -18484,7 +18820,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 355, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -18555,7 +18891,7 @@ "x-appwrite": { "method": "list", "group": null, - "weight": 313, + "weight": 316, "cookies": false, "type": "", "deprecated": false, @@ -18628,7 +18964,7 @@ "x-appwrite": { "method": "createAppwriteMigration", "group": null, - "weight": 308, + "weight": 311, "cookies": false, "type": "", "deprecated": false, @@ -18720,7 +19056,7 @@ "x-appwrite": { "method": "getAppwriteReport", "group": null, - "weight": 315, + "weight": 318, "cookies": false, "type": "", "deprecated": false, @@ -18808,7 +19144,7 @@ "x-appwrite": { "method": "createCsvMigration", "group": null, - "weight": 312, + "weight": 315, "cookies": false, "type": "", "deprecated": false, @@ -18892,7 +19228,7 @@ "x-appwrite": { "method": "createFirebaseMigration", "group": null, - "weight": 309, + "weight": 312, "cookies": false, "type": "", "deprecated": false, @@ -18970,7 +19306,7 @@ "x-appwrite": { "method": "getFirebaseReport", "group": null, - "weight": 316, + "weight": 319, "cookies": false, "type": "", "deprecated": false, @@ -19041,7 +19377,7 @@ "x-appwrite": { "method": "createNHostMigration", "group": null, - "weight": 311, + "weight": 314, "cookies": false, "type": "", "deprecated": false, @@ -19160,7 +19496,7 @@ "x-appwrite": { "method": "getNHostReport", "group": null, - "weight": 318, + "weight": 321, "cookies": false, "type": "", "deprecated": false, @@ -19280,7 +19616,7 @@ "x-appwrite": { "method": "createSupabaseMigration", "group": null, - "weight": 310, + "weight": 313, "cookies": false, "type": "", "deprecated": false, @@ -19392,7 +19728,7 @@ "x-appwrite": { "method": "getSupabaseReport", "group": null, - "weight": 317, + "weight": 320, "cookies": false, "type": "", "deprecated": false, @@ -19503,7 +19839,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 314, + "weight": 317, "cookies": false, "type": "", "deprecated": false, @@ -19561,7 +19897,7 @@ "x-appwrite": { "method": "retry", "group": null, - "weight": 319, + "weight": 322, "cookies": false, "type": "", "deprecated": false, @@ -19614,7 +19950,7 @@ "x-appwrite": { "method": "delete", "group": null, - "weight": 320, + "weight": 323, "cookies": false, "type": "", "deprecated": false, @@ -19672,7 +20008,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 199, + "weight": 202, "cookies": false, "type": "", "deprecated": false, @@ -19754,7 +20090,7 @@ "x-appwrite": { "method": "listVariables", "group": null, - "weight": 201, + "weight": 204, "cookies": false, "type": "", "deprecated": false, @@ -19802,7 +20138,7 @@ "x-appwrite": { "method": "createVariable", "group": null, - "weight": 200, + "weight": 203, "cookies": false, "type": "", "deprecated": false, @@ -19883,7 +20219,7 @@ "x-appwrite": { "method": "getVariable", "group": null, - "weight": 202, + "weight": 205, "cookies": false, "type": "", "deprecated": false, @@ -19941,7 +20277,7 @@ "x-appwrite": { "method": "updateVariable", "group": null, - "weight": 203, + "weight": 206, "cookies": false, "type": "", "deprecated": false, @@ -20024,7 +20360,7 @@ "x-appwrite": { "method": "deleteVariable", "group": null, - "weight": 204, + "weight": 207, "cookies": false, "type": "", "deprecated": false, @@ -20082,7 +20418,7 @@ "x-appwrite": { "method": "list", "group": "projects", - "weight": 154, + "weight": 157, "cookies": false, "type": "", "deprecated": false, @@ -20153,7 +20489,7 @@ "x-appwrite": { "method": "create", "group": "projects", - "weight": 153, + "weight": 156, "cookies": false, "type": "", "deprecated": false, @@ -20300,7 +20636,7 @@ "x-appwrite": { "method": "get", "group": "projects", - "weight": 155, + "weight": 158, "cookies": false, "type": "", "deprecated": false, @@ -20358,7 +20694,7 @@ "x-appwrite": { "method": "update", "group": "projects", - "weight": 156, + "weight": 159, "cookies": false, "type": "", "deprecated": false, @@ -20483,7 +20819,7 @@ "x-appwrite": { "method": "delete", "group": "projects", - "weight": 173, + "weight": 176, "cookies": false, "type": "", "deprecated": false, @@ -20543,7 +20879,7 @@ "x-appwrite": { "method": "updateApiStatus", "group": "projects", - "weight": 160, + "weight": 163, "cookies": false, "type": "", "deprecated": false, @@ -20635,7 +20971,7 @@ "x-appwrite": { "method": "updateApiStatusAll", "group": "projects", - "weight": 161, + "weight": 164, "cookies": false, "type": "", "deprecated": false, @@ -20713,7 +21049,7 @@ "x-appwrite": { "method": "updateAuthDuration", "group": "auth", - "weight": 166, + "weight": 169, "cookies": false, "type": "", "deprecated": false, @@ -20791,7 +21127,7 @@ "x-appwrite": { "method": "updateAuthLimit", "group": "auth", - "weight": 165, + "weight": 168, "cookies": false, "type": "", "deprecated": false, @@ -20869,7 +21205,7 @@ "x-appwrite": { "method": "updateAuthSessionsLimit", "group": "auth", - "weight": 171, + "weight": 174, "cookies": false, "type": "", "deprecated": false, @@ -20947,7 +21283,7 @@ "x-appwrite": { "method": "updateMembershipsPrivacy", "group": "auth", - "weight": 164, + "weight": 167, "cookies": false, "type": "", "deprecated": false, @@ -21039,7 +21375,7 @@ "x-appwrite": { "method": "updateMockNumbers", "group": "auth", - "weight": 172, + "weight": 175, "cookies": false, "type": "", "deprecated": false, @@ -21120,7 +21456,7 @@ "x-appwrite": { "method": "updateAuthPasswordDictionary", "group": "auth", - "weight": 169, + "weight": 172, "cookies": false, "type": "", "deprecated": false, @@ -21198,7 +21534,7 @@ "x-appwrite": { "method": "updateAuthPasswordHistory", "group": "auth", - "weight": 168, + "weight": 171, "cookies": false, "type": "", "deprecated": false, @@ -21276,7 +21612,7 @@ "x-appwrite": { "method": "updatePersonalDataCheck", "group": "auth", - "weight": 170, + "weight": 173, "cookies": false, "type": "", "deprecated": false, @@ -21354,7 +21690,7 @@ "x-appwrite": { "method": "updateSessionAlerts", "group": "auth", - "weight": 163, + "weight": 166, "cookies": false, "type": "", "deprecated": false, @@ -21432,7 +21768,7 @@ "x-appwrite": { "method": "updateAuthStatus", "group": "auth", - "weight": 167, + "weight": 170, "cookies": false, "type": "", "deprecated": false, @@ -21527,7 +21863,7 @@ "x-appwrite": { "method": "listDevKeys", "group": "devKeys", - "weight": 370, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -21597,7 +21933,7 @@ "x-appwrite": { "method": "createDevKey", "group": "devKeys", - "weight": 367, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -21680,7 +22016,7 @@ "x-appwrite": { "method": "getDevKey", "group": "devKeys", - "weight": 369, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -21746,7 +22082,7 @@ "x-appwrite": { "method": "updateDevKey", "group": "devKeys", - "weight": 368, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -21832,7 +22168,7 @@ "x-appwrite": { "method": "deleteDevKey", "group": "devKeys", - "weight": 371, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -21900,7 +22236,7 @@ "x-appwrite": { "method": "createJWT", "group": "auth", - "weight": 185, + "weight": 188, "cookies": false, "type": "", "deprecated": false, @@ -21985,7 +22321,7 @@ "x-appwrite": { "method": "listKeys", "group": "keys", - "weight": 181, + "weight": 184, "cookies": false, "type": "", "deprecated": false, @@ -22043,7 +22379,7 @@ "x-appwrite": { "method": "createKey", "group": "keys", - "weight": 180, + "weight": 183, "cookies": false, "type": "", "deprecated": false, @@ -22135,7 +22471,7 @@ "x-appwrite": { "method": "getKey", "group": "keys", - "weight": 182, + "weight": 185, "cookies": false, "type": "", "deprecated": false, @@ -22201,7 +22537,7 @@ "x-appwrite": { "method": "updateKey", "group": "keys", - "weight": 183, + "weight": 186, "cookies": false, "type": "", "deprecated": false, @@ -22296,7 +22632,7 @@ "x-appwrite": { "method": "deleteKey", "group": "keys", - "weight": 184, + "weight": 187, "cookies": false, "type": "", "deprecated": false, @@ -22364,7 +22700,7 @@ "x-appwrite": { "method": "updateOAuth2", "group": "auth", - "weight": 162, + "weight": 165, "cookies": false, "type": "", "deprecated": false, @@ -22502,7 +22838,7 @@ "x-appwrite": { "method": "listPlatforms", "group": "platforms", - "weight": 187, + "weight": 190, "cookies": false, "type": "", "deprecated": false, @@ -22560,7 +22896,7 @@ "x-appwrite": { "method": "createPlatform", "group": "platforms", - "weight": 186, + "weight": 189, "cookies": false, "type": "", "deprecated": false, @@ -22680,7 +23016,7 @@ "x-appwrite": { "method": "getPlatform", "group": "platforms", - "weight": 188, + "weight": 191, "cookies": false, "type": "", "deprecated": false, @@ -22746,7 +23082,7 @@ "x-appwrite": { "method": "updatePlatform", "group": "platforms", - "weight": 189, + "weight": 192, "cookies": false, "type": "", "deprecated": false, @@ -22843,7 +23179,7 @@ "x-appwrite": { "method": "deletePlatform", "group": "platforms", - "weight": 190, + "weight": 193, "cookies": false, "type": "", "deprecated": false, @@ -22911,7 +23247,7 @@ "x-appwrite": { "method": "updateServiceStatus", "group": "projects", - "weight": 158, + "weight": 161, "cookies": false, "type": "", "deprecated": false, @@ -23012,7 +23348,7 @@ "x-appwrite": { "method": "updateServiceStatusAll", "group": "projects", - "weight": 159, + "weight": 162, "cookies": false, "type": "", "deprecated": false, @@ -23090,7 +23426,7 @@ "x-appwrite": { "method": "updateSmtp", "group": "templates", - "weight": 191, + "weight": 194, "cookies": false, "type": "", "deprecated": false, @@ -23219,7 +23555,7 @@ "x-appwrite": { "method": "createSmtpTest", "group": "templates", - "weight": 192, + "weight": 195, "cookies": false, "type": "", "deprecated": false, @@ -23357,7 +23693,7 @@ "x-appwrite": { "method": "updateTeam", "group": "projects", - "weight": 157, + "weight": 160, "cookies": false, "type": "", "deprecated": false, @@ -23433,7 +23769,7 @@ "x-appwrite": { "method": "getEmailTemplate", "group": "templates", - "weight": 194, + "weight": 197, "cookies": false, "type": "", "deprecated": false, @@ -23653,7 +23989,7 @@ "x-appwrite": { "method": "updateEmailTemplate", "group": "templates", - "weight": 196, + "weight": 199, "cookies": false, "type": "", "deprecated": false, @@ -23916,7 +24252,7 @@ "x-appwrite": { "method": "deleteEmailTemplate", "group": "templates", - "weight": 198, + "weight": 201, "cookies": false, "type": "", "deprecated": false, @@ -24136,7 +24472,7 @@ "x-appwrite": { "method": "getSmsTemplate", "group": "templates", - "weight": 193, + "weight": 196, "cookies": false, "type": "", "deprecated": false, @@ -24353,7 +24689,7 @@ "x-appwrite": { "method": "updateSmsTemplate", "group": "templates", - "weight": 195, + "weight": 198, "cookies": false, "type": "", "deprecated": false, @@ -24588,7 +24924,7 @@ "x-appwrite": { "method": "deleteSmsTemplate", "group": "templates", - "weight": 197, + "weight": 200, "cookies": false, "type": "", "deprecated": false, @@ -24805,7 +25141,7 @@ "x-appwrite": { "method": "listWebhooks", "group": "webhooks", - "weight": 175, + "weight": 178, "cookies": false, "type": "", "deprecated": false, @@ -24863,7 +25199,7 @@ "x-appwrite": { "method": "createWebhook", "group": "webhooks", - "weight": 174, + "weight": 177, "cookies": false, "type": "", "deprecated": false, @@ -24981,7 +25317,7 @@ "x-appwrite": { "method": "getWebhook", "group": "webhooks", - "weight": 176, + "weight": 179, "cookies": false, "type": "", "deprecated": false, @@ -25047,7 +25383,7 @@ "x-appwrite": { "method": "updateWebhook", "group": "webhooks", - "weight": 177, + "weight": 180, "cookies": false, "type": "", "deprecated": false, @@ -25168,7 +25504,7 @@ "x-appwrite": { "method": "deleteWebhook", "group": "webhooks", - "weight": 179, + "weight": 182, "cookies": false, "type": "", "deprecated": false, @@ -25236,7 +25572,7 @@ "x-appwrite": { "method": "updateWebhookSignature", "group": "webhooks", - "weight": 178, + "weight": 181, "cookies": false, "type": "", "deprecated": false, @@ -25302,7 +25638,7 @@ "x-appwrite": { "method": "listRules", "group": null, - "weight": 291, + "weight": 294, "cookies": false, "type": "", "deprecated": false, @@ -25375,7 +25711,7 @@ "x-appwrite": { "method": "createAPIRule", "group": null, - "weight": 432, + "weight": 435, "cookies": false, "type": "", "deprecated": false, @@ -25445,7 +25781,7 @@ "x-appwrite": { "method": "createFunctionRule", "group": null, - "weight": 434, + "weight": 437, "cookies": false, "type": "", "deprecated": false, @@ -25528,7 +25864,7 @@ "x-appwrite": { "method": "createRedirectRule", "group": null, - "weight": 435, + "weight": 438, "cookies": false, "type": "", "deprecated": false, @@ -25588,12 +25924,35 @@ "Temporary Redirect 307", "Permanent Redirect 308" ] + }, + "resourceId": { + "type": "string", + "description": "ID of parent resource.", + "default": null, + "x-example": "" + }, + "resourceType": { + "type": "string", + "description": "Type of parent resource.", + "default": null, + "x-example": "site", + "enum": [ + "site", + "function" + ], + "x-enum-name": "ProxyResourceType", + "x-enum-keys": [ + "Site", + "Function" + ] } }, "required": [ "domain", "url", - "statusCode" + "statusCode", + "resourceId", + "resourceType" ] } } @@ -25625,7 +25984,7 @@ "x-appwrite": { "method": "createSiteRule", "group": null, - "weight": 433, + "weight": 436, "cookies": false, "type": "", "deprecated": false, @@ -25706,7 +26065,7 @@ "x-appwrite": { "method": "getRule", "group": null, - "weight": 292, + "weight": 295, "cookies": false, "type": "", "deprecated": false, @@ -25759,7 +26118,7 @@ "x-appwrite": { "method": "deleteRule", "group": null, - "weight": 293, + "weight": 296, "cookies": false, "type": "", "deprecated": false, @@ -25819,7 +26178,7 @@ "x-appwrite": { "method": "updateRuleVerification", "group": null, - "weight": 294, + "weight": 297, "cookies": false, "type": "", "deprecated": false, @@ -25877,7 +26236,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 404, + "weight": 407, "cookies": false, "type": "", "deprecated": false, @@ -25949,7 +26308,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 402, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -26095,6 +26454,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -26120,7 +26480,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -26214,7 +26575,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 407, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -26263,7 +26624,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 430, + "weight": 433, "cookies": false, "type": "", "deprecated": false, @@ -26313,7 +26674,7 @@ "x-appwrite": { "method": "listTemplates", "group": "templates", - "weight": 426, + "weight": 429, "cookies": false, "type": "", "deprecated": false, @@ -26407,7 +26768,7 @@ "x-appwrite": { "method": "getTemplate", "group": "templates", - "weight": 427, + "weight": 430, "cookies": false, "type": "", "deprecated": false, @@ -26465,7 +26826,7 @@ "x-appwrite": { "method": "listUsage", "group": null, - "weight": 428, + "weight": 431, "cookies": false, "type": "", "deprecated": false, @@ -26535,7 +26896,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 403, + "weight": 406, "cookies": false, "type": "", "deprecated": false, @@ -26594,7 +26955,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 405, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -26742,6 +27103,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -26767,7 +27129,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -26854,7 +27217,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 406, + "weight": 409, "cookies": false, "type": "", "deprecated": false, @@ -26915,7 +27278,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 413, + "weight": 416, "cookies": false, "type": "", "deprecated": false, @@ -26992,7 +27355,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 412, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -27072,7 +27435,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 408, + "weight": 411, "cookies": false, "type": "upload", "deprecated": false, @@ -27172,7 +27535,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 416, + "weight": 419, "cookies": false, "type": "", "deprecated": false, @@ -27251,7 +27614,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 409, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -27357,7 +27720,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 410, + "weight": 413, "cookies": false, "type": "", "deprecated": false, @@ -27454,7 +27817,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 411, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -27516,7 +27879,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 414, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -27583,7 +27946,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 415, + "weight": 418, "cookies": false, "type": "location", "deprecated": false, @@ -27669,7 +28032,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 417, + "weight": 420, "cookies": false, "type": "", "deprecated": false, @@ -27736,7 +28099,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 419, + "weight": 422, "cookies": false, "type": "", "deprecated": false, @@ -27807,7 +28170,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 418, + "weight": 421, "cookies": false, "type": "", "deprecated": false, @@ -27871,7 +28234,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 420, + "weight": 423, "cookies": false, "type": "", "deprecated": false, @@ -27938,7 +28301,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 429, + "weight": 432, "cookies": false, "type": "", "deprecated": false, @@ -28016,7 +28379,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 423, + "weight": 426, "cookies": false, "type": "", "deprecated": false, @@ -28075,7 +28438,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 421, + "weight": 424, "cookies": false, "type": "", "deprecated": false, @@ -28165,7 +28528,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 422, + "weight": 425, "cookies": false, "type": "", "deprecated": false, @@ -28232,7 +28595,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 424, + "weight": 427, "cookies": false, "type": "", "deprecated": false, @@ -28324,7 +28687,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 425, + "weight": 428, "cookies": false, "type": "", "deprecated": false, @@ -28391,7 +28754,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 206, + "weight": 209, "cookies": false, "type": "", "deprecated": false, @@ -28463,7 +28826,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 205, + "weight": 208, "cookies": false, "type": "", "deprecated": false, @@ -28600,7 +28963,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 207, + "weight": 210, "cookies": false, "type": "", "deprecated": false, @@ -28659,7 +29022,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 208, + "weight": 211, "cookies": false, "type": "", "deprecated": false, @@ -28792,7 +29155,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 209, + "weight": 212, "cookies": false, "type": "", "deprecated": false, @@ -28851,7 +29214,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 211, + "weight": 214, "cookies": false, "type": "", "deprecated": false, @@ -28934,7 +29297,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 210, + "weight": 213, "cookies": false, "type": "upload", "deprecated": false, @@ -29024,7 +29387,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 212, + "weight": 215, "cookies": false, "type": "", "deprecated": false, @@ -29094,7 +29457,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 217, + "weight": 220, "cookies": false, "type": "", "deprecated": false, @@ -29183,7 +29546,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 218, + "weight": 221, "cookies": false, "type": "", "deprecated": false, @@ -29253,7 +29616,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 214, + "weight": 217, "cookies": false, "type": "location", "deprecated": false, @@ -29332,7 +29695,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 213, + "weight": 216, "cookies": false, "type": "location", "deprecated": false, @@ -29496,7 +29859,8 @@ "png", "webp", "heic", - "avif" + "avif", + "gif" ], "x-enum-name": "ImageFormat", "x-enum-keys": [], @@ -29538,7 +29902,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 215, + "weight": 218, "cookies": false, "type": "location", "deprecated": false, @@ -29617,7 +29981,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 219, + "weight": 222, "cookies": false, "type": "", "deprecated": false, @@ -29687,7 +30051,7 @@ "x-appwrite": { "method": "getBucketUsage", "group": null, - "weight": 220, + "weight": 223, "cookies": false, "type": "", "deprecated": false, @@ -29765,7 +30129,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 222, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -29840,7 +30204,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 221, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -29930,7 +30294,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 223, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -29992,7 +30356,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 225, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -30067,7 +30431,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 227, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -30129,7 +30493,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 234, + "weight": 237, "cookies": false, "type": "", "deprecated": false, @@ -30199,7 +30563,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 229, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -30282,7 +30646,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 228, + "weight": 231, "cookies": false, "type": "", "deprecated": false, @@ -30395,7 +30759,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 230, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -30465,7 +30829,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 231, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -30551,7 +30915,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 233, + "weight": 236, "cookies": false, "type": "", "deprecated": false, @@ -30623,7 +30987,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 232, + "weight": 235, "cookies": false, "type": "", "deprecated": false, @@ -30716,7 +31080,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 224, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -30776,7 +31140,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 226, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -30854,7 +31218,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 438, + "weight": 441, "cookies": false, "type": "", "deprecated": false, @@ -30922,7 +31286,7 @@ "tags": [ "tokens" ], - "description": "Create a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "description": "Create a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "responses": { "201": { "description": "ResourceToken", @@ -30934,12 +31298,12 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 436, + "weight": 439, "cookies": false, "type": "", "deprecated": false, "demo": "tokens\/create-file-token.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "rate-limit": 60, "rate-time": 60, "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", @@ -31018,7 +31382,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 437, + "weight": 440, "cookies": false, "type": "", "deprecated": false, @@ -31078,7 +31442,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 439, + "weight": 442, "cookies": false, "type": "", "deprecated": false, @@ -31149,7 +31513,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 440, + "weight": 443, "cookies": false, "type": "", "deprecated": false, @@ -31209,7 +31573,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 244, + "weight": 247, "cookies": false, "type": "", "deprecated": false, @@ -31281,7 +31645,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 235, + "weight": 238, "cookies": false, "type": "", "deprecated": false, @@ -31376,7 +31740,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 238, + "weight": 241, "cookies": false, "type": "", "deprecated": false, @@ -31467,7 +31831,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 236, + "weight": 239, "cookies": false, "type": "", "deprecated": false, @@ -31556,7 +31920,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 252, + "weight": 255, "cookies": false, "type": "", "deprecated": false, @@ -31625,7 +31989,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 275, + "weight": 278, "cookies": false, "type": "", "deprecated": false, @@ -31686,7 +32050,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 237, + "weight": 240, "cookies": false, "type": "", "deprecated": false, @@ -31777,7 +32141,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 240, + "weight": 243, "cookies": false, "type": "", "deprecated": false, @@ -31868,7 +32232,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 241, + "weight": 244, "cookies": false, "type": "", "deprecated": false, @@ -31994,7 +32358,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 242, + "weight": 245, "cookies": false, "type": "", "deprecated": false, @@ -32106,7 +32470,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 239, + "weight": 242, "cookies": false, "type": "", "deprecated": false, @@ -32216,7 +32580,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 277, + "weight": 280, "cookies": false, "type": "", "deprecated": false, @@ -32286,7 +32650,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 245, + "weight": 248, "cookies": false, "type": "", "deprecated": false, @@ -32340,7 +32704,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 273, + "weight": 276, "cookies": false, "type": "", "deprecated": false, @@ -32401,7 +32765,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 258, + "weight": 261, "cookies": false, "type": "", "deprecated": false, @@ -32480,7 +32844,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 276, + "weight": 279, "cookies": false, "type": "", "deprecated": false, @@ -32562,7 +32926,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 254, + "weight": 257, "cookies": false, "type": "", "deprecated": false, @@ -32642,7 +33006,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 250, + "weight": 253, "cookies": false, "type": "", "deprecated": false, @@ -32713,7 +33077,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 249, + "weight": 252, "cookies": false, "type": "", "deprecated": false, @@ -32795,7 +33159,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 263, + "weight": 266, "cookies": false, "type": "", "deprecated": false, @@ -32869,7 +33233,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 268, + "weight": 271, "cookies": false, "type": "", "deprecated": false, @@ -32941,7 +33305,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 264, + "weight": 267, "cookies": false, "type": "", "deprecated": false, @@ -33000,7 +33364,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 265, + "weight": 268, "cookies": false, "type": "", "deprecated": false, @@ -33059,7 +33423,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 267, + "weight": 270, "cookies": false, "type": "", "deprecated": false, @@ -33118,7 +33482,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 266, + "weight": 269, "cookies": false, "type": "", "deprecated": false, @@ -33179,7 +33543,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 256, + "weight": 259, "cookies": false, "type": "", "deprecated": false, @@ -33258,7 +33622,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 257, + "weight": 260, "cookies": false, "type": "", "deprecated": false, @@ -33337,7 +33701,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 259, + "weight": 262, "cookies": false, "type": "", "deprecated": false, @@ -33414,7 +33778,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 246, + "weight": 249, "cookies": false, "type": "", "deprecated": false, @@ -33473,7 +33837,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 261, + "weight": 264, "cookies": false, "type": "", "deprecated": false, @@ -33550,7 +33914,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 248, + "weight": 251, "cookies": false, "type": "", "deprecated": false, @@ -33609,7 +33973,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 269, + "weight": 272, "cookies": false, "type": "", "deprecated": false, @@ -33663,7 +34027,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 272, + "weight": 275, "cookies": false, "type": "", "deprecated": false, @@ -33719,7 +34083,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 271, + "weight": 274, "cookies": false, "type": "", "deprecated": false, @@ -33788,7 +34152,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 253, + "weight": 256, "cookies": false, "type": "", "deprecated": false, @@ -33865,7 +34229,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 251, + "weight": 254, "cookies": false, "type": "", "deprecated": false, @@ -33901,7 +34265,7 @@ }, { "name": "queries", - "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: name, email, phone, status, passwordUpdate, registration, emailVerification, phoneVerification, labels", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, providerId, identifier, providerType", "required": false, "type": "array", "collectionFormat": "multi", @@ -33937,7 +34301,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 243, + "weight": 246, "cookies": false, "type": "", "deprecated": false, @@ -34048,7 +34412,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 247, + "weight": 250, "cookies": false, "type": "", "deprecated": false, @@ -34116,7 +34480,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 262, + "weight": 265, "cookies": false, "type": "", "deprecated": false, @@ -34206,7 +34570,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 274, + "weight": 277, "cookies": false, "type": "", "deprecated": false, @@ -34276,7 +34640,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 270, + "weight": 273, "cookies": false, "type": "", "deprecated": false, @@ -34358,7 +34722,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 260, + "weight": 263, "cookies": false, "type": "", "deprecated": false, @@ -34437,7 +34801,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 255, + "weight": 258, "cookies": false, "type": "", "deprecated": false, @@ -34516,7 +34880,7 @@ "x-appwrite": { "method": "createRepositoryDetection", "group": "repositories", - "weight": 281, + "weight": 284, "cookies": false, "type": "", "deprecated": false, @@ -34611,7 +34975,7 @@ "x-appwrite": { "method": "listRepositories", "group": "repositories", - "weight": 282, + "weight": 285, "cookies": false, "type": "", "deprecated": false, @@ -34692,7 +35056,7 @@ "x-appwrite": { "method": "createRepository", "group": "repositories", - "weight": 283, + "weight": 286, "cookies": false, "type": "", "deprecated": false, @@ -34775,7 +35139,7 @@ "x-appwrite": { "method": "getRepository", "group": "repositories", - "weight": 284, + "weight": 287, "cookies": false, "type": "", "deprecated": false, @@ -34841,7 +35205,7 @@ "x-appwrite": { "method": "listRepositoryBranches", "group": "repositories", - "weight": 285, + "weight": 288, "cookies": false, "type": "", "deprecated": false, @@ -34907,7 +35271,7 @@ "x-appwrite": { "method": "getRepositoryContents", "group": "repositories", - "weight": 280, + "weight": 283, "cookies": false, "type": "", "deprecated": false, @@ -34981,7 +35345,7 @@ "x-appwrite": { "method": "updateExternalDeployments", "group": "repositories", - "weight": 290, + "weight": 293, "cookies": false, "type": "", "deprecated": false, @@ -35065,7 +35429,7 @@ "x-appwrite": { "method": "listInstallations", "group": "installations", - "weight": 287, + "weight": 290, "cookies": false, "type": "", "deprecated": false, @@ -35136,7 +35500,7 @@ "x-appwrite": { "method": "getInstallation", "group": "installations", - "weight": 288, + "weight": 291, "cookies": false, "type": "", "deprecated": false, @@ -35189,7 +35553,7 @@ "x-appwrite": { "method": "deleteInstallation", "group": "installations", - "weight": 289, + "weight": 292, "cookies": false, "type": "", "deprecated": false, @@ -36711,6 +37075,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -37462,6 +37832,12 @@ "description": "Document ID.", "x-example": "5e5ea5c16897e" }, + "$sequence": { + "type": "integer", + "description": "Document automatically incrementing ID.", + "x-example": 1, + "format": "int32" + }, "$collectionId": { "type": "string", "description": "Collection ID.", @@ -37496,6 +37872,7 @@ "additionalProperties": true, "required": [ "$id", + "$sequence", "$collectionId", "$databaseId", "$createdAt", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index d2924041c0..7c15f9e5ea 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "version": "1.7.0", + "version": "1.7.4", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -7616,6 +7616,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "client", "server", "server" @@ -7625,6 +7626,7 @@ { "name": "createDocument", "auth": { + "Admin": [], "Session": [], "Key": [], "JWT": [] @@ -7653,6 +7655,7 @@ { "name": "createDocuments", "auth": { + "Admin": [], "Key": [] }, "parameters": [ @@ -7769,7 +7772,7 @@ "x-appwrite": { "method": "upsertDocuments", "group": "documents", - "weight": 115, + "weight": 118, "cookies": false, "type": "", "deprecated": false, @@ -7780,6 +7783,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "server" ], "packaging": false, @@ -7820,13 +7824,16 @@ "documents": { "type": "array", "description": "Array of document data as JSON objects. May contain partial documents.", - "default": [], + "default": null, "x-example": null, "items": { "type": "object" } } - } + }, + "required": [ + "documents" + ] } } ] @@ -7855,7 +7862,7 @@ "x-appwrite": { "method": "updateDocuments", "group": "documents", - "weight": 114, + "weight": 117, "cookies": false, "type": "", "deprecated": false, @@ -7866,6 +7873,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "server" ], "packaging": false, @@ -7947,7 +7955,7 @@ "x-appwrite": { "method": "deleteDocuments", "group": "documents", - "weight": 117, + "weight": 120, "cookies": false, "type": "", "deprecated": false, @@ -7958,6 +7966,7 @@ "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", "scope": "documents.write", "platforms": [ + "console", "server" ], "packaging": false, @@ -8101,6 +8110,113 @@ } ] }, + "put": { + "summary": "Upsert document", + "operationId": "databasesUpsertDocument", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Create or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "responses": { + "200": { + "description": "Document", + "schema": { + "$ref": "#\/definitions\/document" + } + } + }, + "x-appwrite": { + "method": "upsertDocument", + "group": "documents", + "weight": 114, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/upsert-document.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/upsert-document.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "client", + "server", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Session": [] + } + }, + "security": [ + { + "Project": [], + "Session": [], + "Key": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Document data as JSON object. Include all required attributes of the document to be created or updated.", + "default": {}, + "x-example": "{}" + }, + "permissions": { + "type": "array", + "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", + "default": null, + "x-example": "[\"read(\"any\")\"]", + "items": { + "type": "string" + } + } + }, + "required": [ + "data" + ] + } + } + ] + }, "patch": { "summary": "Update document", "operationId": "databasesUpdateDocument", @@ -8224,7 +8340,7 @@ "x-appwrite": { "method": "deleteDocument", "group": "documents", - "weight": 116, + "weight": 119, "cookies": false, "type": "", "deprecated": false, @@ -8281,6 +8397,228 @@ ] } }, + "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/decrement": { + "patch": { + "summary": "Decrement document attribute", + "operationId": "databasesDecrementDocumentAttribute", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Decrement a specific attribute of a document by a given value.", + "responses": { + "200": { + "description": "Document", + "schema": { + "$ref": "#\/definitions\/document" + } + } + }, + "x-appwrite": { + "method": "decrementDocumentAttribute", + "group": "documents", + "weight": 116, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/decrement-document-attribute.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/decrement-document-attribute.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "console", + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "attribute", + "description": "Attribute key.", + "required": true, + "type": "string", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "value": { + "type": "number", + "description": "Value to decrement the attribute by. The value must be a number.", + "default": 1, + "x-example": null + }, + "min": { + "type": "number", + "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", + "default": null, + "x-example": null + } + } + } + } + ] + } + }, + "\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/increment": { + "patch": { + "summary": "Increment document attribute", + "operationId": "databasesIncrementDocumentAttribute", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "databases" + ], + "description": "Increment a specific attribute of a document by a given value.", + "responses": { + "200": { + "description": "Document", + "schema": { + "$ref": "#\/definitions\/document" + } + } + }, + "x-appwrite": { + "method": "incrementDocumentAttribute", + "group": "documents", + "weight": 115, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "databases\/increment-document-attribute.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/increment-document-attribute.md", + "rate-limit": 120, + "rate-time": 60, + "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", + "scope": "documents.write", + "platforms": [ + "console", + "server", + "client", + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [], + "Session": [], + "JWT": [] + } + ], + "parameters": [ + { + "name": "databaseId", + "description": "Database ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "collectionId", + "description": "Collection ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "documentId", + "description": "Document ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "attribute", + "description": "Attribute key.", + "required": true, + "type": "string", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "value": { + "type": "number", + "description": "Value to increment the attribute by. The value must be a number.", + "default": 1, + "x-example": null + }, + "max": { + "type": "number", + "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", + "default": null, + "x-example": null + } + } + } + } + ] + } + }, "\/databases\/{databaseId}\/collections\/{collectionId}\/indexes": { "get": { "summary": "List indexes", @@ -8657,7 +8995,7 @@ "x-appwrite": { "method": "list", "group": "functions", - "weight": 375, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -8730,7 +9068,7 @@ "x-appwrite": { "method": "create", "group": "functions", - "weight": 372, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -8817,6 +9155,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -8842,7 +9181,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -8980,7 +9320,7 @@ "x-appwrite": { "method": "listRuntimes", "group": "runtimes", - "weight": 377, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -9030,7 +9370,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "runtimes", - "weight": 378, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -9081,7 +9421,7 @@ "x-appwrite": { "method": "get", "group": "functions", - "weight": 373, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -9141,7 +9481,7 @@ "x-appwrite": { "method": "update", "group": "functions", - "weight": 374, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -9230,6 +9570,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -9255,7 +9596,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -9387,7 +9729,7 @@ "x-appwrite": { "method": "delete", "group": "functions", - "weight": 376, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -9449,7 +9791,7 @@ "x-appwrite": { "method": "updateFunctionDeployment", "group": "functions", - "weight": 381, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -9527,7 +9869,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 382, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -9608,7 +9950,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 379, + "weight": 382, "cookies": false, "type": "upload", "deprecated": false, @@ -9701,7 +10043,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 387, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -9787,7 +10129,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 384, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -9894,7 +10236,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 385, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -9991,7 +10333,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 380, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -10054,7 +10396,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 383, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -10122,7 +10464,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 386, + "weight": 389, "cookies": false, "type": "location", "deprecated": false, @@ -10209,7 +10551,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 388, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -10277,7 +10619,7 @@ "x-appwrite": { "method": "listExecutions", "group": "executions", - "weight": 391, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -10353,7 +10695,7 @@ "x-appwrite": { "method": "createExecution", "group": "executions", - "weight": 389, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -10472,7 +10814,7 @@ "x-appwrite": { "method": "getExecution", "group": "executions", - "weight": 390, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -10539,7 +10881,7 @@ "x-appwrite": { "method": "deleteExecution", "group": "executions", - "weight": 392, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -10607,7 +10949,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 397, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -10667,7 +11009,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 395, + "weight": 398, "cookies": false, "type": "", "deprecated": false, @@ -10758,7 +11100,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 396, + "weight": 399, "cookies": false, "type": "", "deprecated": false, @@ -10826,7 +11168,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 398, + "weight": 401, "cookies": false, "type": "", "deprecated": false, @@ -10919,7 +11261,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 399, + "weight": 402, "cookies": false, "type": "", "deprecated": false, @@ -10989,7 +11331,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 305, + "weight": 308, "cookies": false, "type": "graphql", "deprecated": false, @@ -11065,7 +11407,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 304, + "weight": 307, "cookies": false, "type": "graphql", "deprecated": false, @@ -11139,7 +11481,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 129, + "weight": 132, "cookies": false, "type": "", "deprecated": false, @@ -11189,7 +11531,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 150, + "weight": 153, "cookies": false, "type": "", "deprecated": false, @@ -11239,7 +11581,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 132, + "weight": 135, "cookies": false, "type": "", "deprecated": false, @@ -11289,7 +11631,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 137, + "weight": 140, "cookies": false, "type": "", "deprecated": false, @@ -11348,7 +11690,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 131, + "weight": 134, "cookies": false, "type": "", "deprecated": false, @@ -11398,7 +11740,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 133, + "weight": 136, "cookies": false, "type": "", "deprecated": false, @@ -11448,7 +11790,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 139, + "weight": 142, "cookies": false, "type": "", "deprecated": false, @@ -11509,7 +11851,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 138, + "weight": 141, "cookies": false, "type": "", "deprecated": false, @@ -11570,7 +11912,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 140, + "weight": 143, "cookies": false, "type": "", "deprecated": false, @@ -11640,7 +11982,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 141, + "weight": 144, "cookies": false, "type": "", "deprecated": false, @@ -11701,7 +12043,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 151, + "weight": 154, "cookies": false, "type": "", "deprecated": false, @@ -11786,7 +12128,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 145, + "weight": 148, "cookies": false, "type": "", "deprecated": false, @@ -11847,7 +12189,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 136, + "weight": 139, "cookies": false, "type": "", "deprecated": false, @@ -11908,7 +12250,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 142, + "weight": 145, "cookies": false, "type": "", "deprecated": false, @@ -11969,7 +12311,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 143, + "weight": 146, "cookies": false, "type": "", "deprecated": false, @@ -12030,7 +12372,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 144, + "weight": 147, "cookies": false, "type": "", "deprecated": false, @@ -12091,7 +12433,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 146, + "weight": 149, "cookies": false, "type": "", "deprecated": false, @@ -12152,7 +12494,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 147, + "weight": 150, "cookies": false, "type": "", "deprecated": false, @@ -12213,7 +12555,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 135, + "weight": 138, "cookies": false, "type": "", "deprecated": false, @@ -12274,7 +12616,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 149, + "weight": 152, "cookies": false, "type": "", "deprecated": false, @@ -12324,7 +12666,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 148, + "weight": 151, "cookies": false, "type": "", "deprecated": false, @@ -12374,7 +12716,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 134, + "weight": 137, "cookies": false, "type": "", "deprecated": false, @@ -12424,7 +12766,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 121, + "weight": 124, "cookies": false, "type": "", "deprecated": false, @@ -12478,7 +12820,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 122, + "weight": 125, "cookies": false, "type": "", "deprecated": false, @@ -12532,7 +12874,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 126, + "weight": 129, "cookies": false, "type": "", "deprecated": false, @@ -12586,7 +12928,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 123, + "weight": 126, "cookies": false, "type": "", "deprecated": false, @@ -12640,7 +12982,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 124, + "weight": 127, "cookies": false, "type": "", "deprecated": false, @@ -12694,7 +13036,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 125, + "weight": 128, "cookies": false, "type": "", "deprecated": false, @@ -12748,7 +13090,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 127, + "weight": 130, "cookies": false, "type": "", "deprecated": false, @@ -12802,7 +13144,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 128, + "weight": 131, "cookies": false, "type": "", "deprecated": false, @@ -12856,7 +13198,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 359, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -12932,7 +13274,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 356, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -13091,7 +13433,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 363, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -13247,7 +13589,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 358, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -13443,7 +13785,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 365, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -13638,7 +13980,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 357, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -13757,7 +14099,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 364, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -13872,7 +14214,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 362, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -13928,7 +14270,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 366, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -13989,7 +14331,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 360, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -14062,7 +14404,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 361, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -14135,7 +14477,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 331, + "weight": 334, "cookies": false, "type": "", "deprecated": false, @@ -14211,7 +14553,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 330, + "weight": 333, "cookies": false, "type": "", "deprecated": false, @@ -14327,7 +14669,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 343, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -14441,7 +14783,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 329, + "weight": 332, "cookies": false, "type": "", "deprecated": false, @@ -14533,7 +14875,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 342, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -14623,7 +14965,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 321, + "weight": 324, "cookies": false, "type": "", "deprecated": false, @@ -14751,7 +15093,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 334, + "weight": 337, "cookies": false, "type": "", "deprecated": false, @@ -14877,7 +15219,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 324, + "weight": 327, "cookies": false, "type": "", "deprecated": false, @@ -14981,7 +15323,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 337, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -15083,7 +15425,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 322, + "weight": 325, "cookies": false, "type": "", "deprecated": false, @@ -15199,7 +15541,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 335, + "weight": 338, "cookies": false, "type": "", "deprecated": false, @@ -15313,7 +15655,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 323, + "weight": 326, "cookies": false, "type": "", "deprecated": false, @@ -15473,7 +15815,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 336, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -15630,7 +15972,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 325, + "weight": 328, "cookies": false, "type": "", "deprecated": false, @@ -15734,7 +16076,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 338, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -15836,7 +16178,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 326, + "weight": 329, "cookies": false, "type": "", "deprecated": false, @@ -15940,7 +16282,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 339, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -16042,7 +16384,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 327, + "weight": 330, "cookies": false, "type": "", "deprecated": false, @@ -16146,7 +16488,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 340, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -16248,7 +16590,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 328, + "weight": 331, "cookies": false, "type": "", "deprecated": false, @@ -16352,7 +16694,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 341, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -16452,7 +16794,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 333, + "weight": 336, "cookies": false, "type": "", "deprecated": false, @@ -16508,7 +16850,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 344, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -16569,7 +16911,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 332, + "weight": 335, "cookies": false, "type": "", "deprecated": false, @@ -16642,7 +16984,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 353, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -16715,7 +17057,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 346, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -16789,7 +17131,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 345, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -16878,7 +17220,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 348, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -16939,7 +17281,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 349, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -17019,7 +17361,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 350, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -17080,7 +17422,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 347, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -17153,7 +17495,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 352, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -17235,7 +17577,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 351, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -17325,7 +17667,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 354, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -17389,7 +17731,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 355, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -17462,7 +17804,7 @@ "x-appwrite": { "method": "list", "group": "sites", - "weight": 404, + "weight": 407, "cookies": false, "type": "", "deprecated": false, @@ -17535,7 +17877,7 @@ "x-appwrite": { "method": "create", "group": "sites", - "weight": 402, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -17682,6 +18024,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -17707,7 +18050,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -17801,7 +18145,7 @@ "x-appwrite": { "method": "listFrameworks", "group": "frameworks", - "weight": 407, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -17851,7 +18195,7 @@ "x-appwrite": { "method": "listSpecifications", "group": "frameworks", - "weight": 430, + "weight": 433, "cookies": false, "type": "", "deprecated": false, @@ -17902,7 +18246,7 @@ "x-appwrite": { "method": "get", "group": "sites", - "weight": 403, + "weight": 406, "cookies": false, "type": "", "deprecated": false, @@ -17962,7 +18306,7 @@ "x-appwrite": { "method": "update", "group": "sites", - "weight": 405, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -18111,6 +18455,7 @@ "dart-3.1", "dart-3.3", "dart-3.5", + "dart-3.8", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -18136,7 +18481,8 @@ "static-1", "flutter-3.24", "flutter-3.27", - "flutter-3.29" + "flutter-3.29", + "flutter-3.32" ], "x-enum-name": null, "x-enum-keys": [] @@ -18223,7 +18569,7 @@ "x-appwrite": { "method": "delete", "group": "sites", - "weight": 406, + "weight": 409, "cookies": false, "type": "", "deprecated": false, @@ -18285,7 +18631,7 @@ "x-appwrite": { "method": "updateSiteDeployment", "group": "sites", - "weight": 413, + "weight": 416, "cookies": false, "type": "", "deprecated": false, @@ -18363,7 +18709,7 @@ "x-appwrite": { "method": "listDeployments", "group": "deployments", - "weight": 412, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -18444,7 +18790,7 @@ "x-appwrite": { "method": "createDeployment", "group": "deployments", - "weight": 408, + "weight": 411, "cookies": false, "type": "upload", "deprecated": false, @@ -18545,7 +18891,7 @@ "x-appwrite": { "method": "createDuplicateDeployment", "group": "deployments", - "weight": 416, + "weight": 419, "cookies": false, "type": "", "deprecated": false, @@ -18625,7 +18971,7 @@ "x-appwrite": { "method": "createTemplateDeployment", "group": "deployments", - "weight": 409, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -18732,7 +19078,7 @@ "x-appwrite": { "method": "createVcsDeployment", "group": "deployments", - "weight": 410, + "weight": 413, "cookies": false, "type": "", "deprecated": false, @@ -18830,7 +19176,7 @@ "x-appwrite": { "method": "getDeployment", "group": "deployments", - "weight": 411, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -18893,7 +19239,7 @@ "x-appwrite": { "method": "deleteDeployment", "group": "deployments", - "weight": 414, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -18961,7 +19307,7 @@ "x-appwrite": { "method": "getDeploymentDownload", "group": "deployments", - "weight": 415, + "weight": 418, "cookies": false, "type": "location", "deprecated": false, @@ -19048,7 +19394,7 @@ "x-appwrite": { "method": "updateDeploymentStatus", "group": "deployments", - "weight": 417, + "weight": 420, "cookies": false, "type": "", "deprecated": false, @@ -19116,7 +19462,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 419, + "weight": 422, "cookies": false, "type": "", "deprecated": false, @@ -19188,7 +19534,7 @@ "x-appwrite": { "method": "getLog", "group": "logs", - "weight": 418, + "weight": 421, "cookies": false, "type": "", "deprecated": false, @@ -19253,7 +19599,7 @@ "x-appwrite": { "method": "deleteLog", "group": "logs", - "weight": 420, + "weight": 423, "cookies": false, "type": "", "deprecated": false, @@ -19321,7 +19667,7 @@ "x-appwrite": { "method": "listVariables", "group": "variables", - "weight": 423, + "weight": 426, "cookies": false, "type": "", "deprecated": false, @@ -19381,7 +19727,7 @@ "x-appwrite": { "method": "createVariable", "group": "variables", - "weight": 421, + "weight": 424, "cookies": false, "type": "", "deprecated": false, @@ -19472,7 +19818,7 @@ "x-appwrite": { "method": "getVariable", "group": "variables", - "weight": 422, + "weight": 425, "cookies": false, "type": "", "deprecated": false, @@ -19540,7 +19886,7 @@ "x-appwrite": { "method": "updateVariable", "group": "variables", - "weight": 424, + "weight": 427, "cookies": false, "type": "", "deprecated": false, @@ -19633,7 +19979,7 @@ "x-appwrite": { "method": "deleteVariable", "group": "variables", - "weight": 425, + "weight": 428, "cookies": false, "type": "", "deprecated": false, @@ -19701,7 +20047,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 206, + "weight": 209, "cookies": false, "type": "", "deprecated": false, @@ -19774,7 +20120,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 205, + "weight": 208, "cookies": false, "type": "", "deprecated": false, @@ -19912,7 +20258,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 207, + "weight": 210, "cookies": false, "type": "", "deprecated": false, @@ -19972,7 +20318,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 208, + "weight": 211, "cookies": false, "type": "", "deprecated": false, @@ -20106,7 +20452,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 209, + "weight": 212, "cookies": false, "type": "", "deprecated": false, @@ -20166,7 +20512,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 211, + "weight": 214, "cookies": false, "type": "", "deprecated": false, @@ -20251,7 +20597,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 210, + "weight": 213, "cookies": false, "type": "upload", "deprecated": false, @@ -20343,7 +20689,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 212, + "weight": 215, "cookies": false, "type": "", "deprecated": false, @@ -20415,7 +20761,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 217, + "weight": 220, "cookies": false, "type": "", "deprecated": false, @@ -20506,7 +20852,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 218, + "weight": 221, "cookies": false, "type": "", "deprecated": false, @@ -20578,7 +20924,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 214, + "weight": 217, "cookies": false, "type": "location", "deprecated": false, @@ -20659,7 +21005,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 213, + "weight": 216, "cookies": false, "type": "location", "deprecated": false, @@ -20825,7 +21171,8 @@ "png", "webp", "heic", - "avif" + "avif", + "gif" ], "x-enum-name": "ImageFormat", "x-enum-keys": [], @@ -20867,7 +21214,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 215, + "weight": 218, "cookies": false, "type": "location", "deprecated": false, @@ -20948,7 +21295,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 222, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -21025,7 +21372,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 221, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -21117,7 +21464,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 223, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -21181,7 +21528,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 225, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -21258,7 +21605,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 227, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -21322,7 +21669,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 229, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -21407,7 +21754,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 228, + "weight": 231, "cookies": false, "type": "", "deprecated": false, @@ -21522,7 +21869,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 230, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -21594,7 +21941,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 231, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -21682,7 +22029,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 233, + "weight": 236, "cookies": false, "type": "", "deprecated": false, @@ -21756,7 +22103,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 232, + "weight": 235, "cookies": false, "type": "", "deprecated": false, @@ -21851,7 +22198,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 224, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -21913,7 +22260,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 226, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -21993,7 +22340,7 @@ "x-appwrite": { "method": "list", "group": "files", - "weight": 438, + "weight": 441, "cookies": false, "type": "", "deprecated": false, @@ -22062,7 +22409,7 @@ "tags": [ "tokens" ], - "description": "Create a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "description": "Create a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "responses": { "201": { "description": "ResourceToken", @@ -22074,12 +22421,12 @@ "x-appwrite": { "method": "createFileToken", "group": "files", - "weight": 436, + "weight": 439, "cookies": false, "type": "", "deprecated": false, "demo": "tokens\/create-file-token.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a header or request get parameter.", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new token. A token is linked to a file. Token can be passed as a request URL search parameter.", "rate-limit": 60, "rate-time": 60, "rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}", @@ -22159,7 +22506,7 @@ "x-appwrite": { "method": "get", "group": "tokens", - "weight": 437, + "weight": 440, "cookies": false, "type": "", "deprecated": false, @@ -22220,7 +22567,7 @@ "x-appwrite": { "method": "update", "group": "tokens", - "weight": 439, + "weight": 442, "cookies": false, "type": "", "deprecated": false, @@ -22292,7 +22639,7 @@ "x-appwrite": { "method": "delete", "group": "tokens", - "weight": 440, + "weight": 443, "cookies": false, "type": "", "deprecated": false, @@ -22353,7 +22700,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 244, + "weight": 247, "cookies": false, "type": "", "deprecated": false, @@ -22426,7 +22773,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 235, + "weight": 238, "cookies": false, "type": "", "deprecated": false, @@ -22522,7 +22869,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 238, + "weight": 241, "cookies": false, "type": "", "deprecated": false, @@ -22614,7 +22961,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 236, + "weight": 239, "cookies": false, "type": "", "deprecated": false, @@ -22704,7 +23051,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 252, + "weight": 255, "cookies": false, "type": "", "deprecated": false, @@ -22774,7 +23121,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 275, + "weight": 278, "cookies": false, "type": "", "deprecated": false, @@ -22836,7 +23183,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 237, + "weight": 240, "cookies": false, "type": "", "deprecated": false, @@ -22928,7 +23275,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 240, + "weight": 243, "cookies": false, "type": "", "deprecated": false, @@ -23020,7 +23367,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 241, + "weight": 244, "cookies": false, "type": "", "deprecated": false, @@ -23147,7 +23494,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 242, + "weight": 245, "cookies": false, "type": "", "deprecated": false, @@ -23260,7 +23607,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 239, + "weight": 242, "cookies": false, "type": "", "deprecated": false, @@ -23371,7 +23718,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 245, + "weight": 248, "cookies": false, "type": "", "deprecated": false, @@ -23426,7 +23773,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 273, + "weight": 276, "cookies": false, "type": "", "deprecated": false, @@ -23488,7 +23835,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 258, + "weight": 261, "cookies": false, "type": "", "deprecated": false, @@ -23568,7 +23915,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 276, + "weight": 279, "cookies": false, "type": "", "deprecated": false, @@ -23651,7 +23998,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 254, + "weight": 257, "cookies": false, "type": "", "deprecated": false, @@ -23732,7 +24079,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 250, + "weight": 253, "cookies": false, "type": "", "deprecated": false, @@ -23804,7 +24151,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 249, + "weight": 252, "cookies": false, "type": "", "deprecated": false, @@ -23887,7 +24234,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 263, + "weight": 266, "cookies": false, "type": "", "deprecated": false, @@ -23962,7 +24309,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 268, + "weight": 271, "cookies": false, "type": "", "deprecated": false, @@ -24035,7 +24382,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 264, + "weight": 267, "cookies": false, "type": "", "deprecated": false, @@ -24095,7 +24442,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 265, + "weight": 268, "cookies": false, "type": "", "deprecated": false, @@ -24155,7 +24502,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 267, + "weight": 270, "cookies": false, "type": "", "deprecated": false, @@ -24215,7 +24562,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 266, + "weight": 269, "cookies": false, "type": "", "deprecated": false, @@ -24277,7 +24624,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 256, + "weight": 259, "cookies": false, "type": "", "deprecated": false, @@ -24357,7 +24704,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 257, + "weight": 260, "cookies": false, "type": "", "deprecated": false, @@ -24437,7 +24784,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 259, + "weight": 262, "cookies": false, "type": "", "deprecated": false, @@ -24515,7 +24862,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 246, + "weight": 249, "cookies": false, "type": "", "deprecated": false, @@ -24575,7 +24922,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 261, + "weight": 264, "cookies": false, "type": "", "deprecated": false, @@ -24653,7 +25000,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 248, + "weight": 251, "cookies": false, "type": "", "deprecated": false, @@ -24713,7 +25060,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 269, + "weight": 272, "cookies": false, "type": "", "deprecated": false, @@ -24768,7 +25115,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 272, + "weight": 275, "cookies": false, "type": "", "deprecated": false, @@ -24825,7 +25172,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 271, + "weight": 274, "cookies": false, "type": "", "deprecated": false, @@ -24895,7 +25242,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 253, + "weight": 256, "cookies": false, "type": "", "deprecated": false, @@ -24973,7 +25320,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 251, + "weight": 254, "cookies": false, "type": "", "deprecated": false, @@ -25010,7 +25357,7 @@ }, { "name": "queries", - "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: name, email, phone, status, passwordUpdate, registration, emailVerification, phoneVerification, labels", + "description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, providerId, identifier, providerType", "required": false, "type": "array", "collectionFormat": "multi", @@ -25046,7 +25393,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 243, + "weight": 246, "cookies": false, "type": "", "deprecated": false, @@ -25158,7 +25505,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 247, + "weight": 250, "cookies": false, "type": "", "deprecated": false, @@ -25227,7 +25574,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 262, + "weight": 265, "cookies": false, "type": "", "deprecated": false, @@ -25318,7 +25665,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 274, + "weight": 277, "cookies": false, "type": "", "deprecated": false, @@ -25389,7 +25736,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 270, + "weight": 273, "cookies": false, "type": "", "deprecated": false, @@ -25472,7 +25819,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 260, + "weight": 263, "cookies": false, "type": "", "deprecated": false, @@ -25552,7 +25899,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 255, + "weight": 258, "cookies": false, "type": "", "deprecated": false, @@ -26744,6 +27091,12 @@ "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "x-example": "default", "x-nullable": true + }, + "encrypt": { + "type": "boolean", + "description": "Defines whether this attribute is encrypted or not.", + "x-example": false, + "x-nullable": true } }, "required": [ @@ -27495,6 +27848,12 @@ "description": "Document ID.", "x-example": "5e5ea5c16897e" }, + "$sequence": { + "type": "integer", + "description": "Document automatically incrementing ID.", + "x-example": 1, + "format": "int32" + }, "$collectionId": { "type": "string", "description": "Collection ID.", @@ -27529,6 +27888,7 @@ "additionalProperties": true, "required": [ "$id", + "$sequence", "$collectionId", "$databaseId", "$createdAt", diff --git a/app/config/storage/inputs.php b/app/config/storage/inputs.php index edcf667d86..a102dfdcf0 100644 --- a/app/config/storage/inputs.php +++ b/app/config/storage/inputs.php @@ -7,4 +7,5 @@ return [ "png" => "image/png", "heic" => "image/heic", "webp" => "image/webp", + "gif" => "image/gif", ]; diff --git a/app/config/storage/outputs.php b/app/config/storage/outputs.php index 519ff825fe..3e6fd45651 100644 --- a/app/config/storage/outputs.php +++ b/app/config/storage/outputs.php @@ -8,4 +8,5 @@ return [ "webp" => "image/webp", "heic" => "image/heic", "avif" => "image/avif", + "gif" => "image/gif", ]; diff --git a/app/config/template-runtimes.php b/app/config/template-runtimes.php index 8f1c0198c2..d1bb1a5b6a 100644 --- a/app/config/template-runtimes.php +++ b/app/config/template-runtimes.php @@ -14,7 +14,7 @@ return [ ], 'DART' => [ 'name' => 'dart', - 'versions' => ['3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16'] + 'versions' => ['3.8', '3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16'] ], 'GO' => [ 'name' => 'go', @@ -38,6 +38,6 @@ return [ ], 'FLUTTER' => [ 'name' => 'flutter', - 'versions' => ['3.24'] + 'versions' => ['3.32', '3.24'] ], ]; diff --git a/app/config/templates/site.php b/app/config/templates/site.php index eec129a7bf..a853955a97 100644 --- a/app/config/templates/site.php +++ b/app/config/templates/site.php @@ -478,7 +478,7 @@ return [ 'frameworks' => [ getFramework('FLUTTER', [ 'providerRootDirectory' => './', - 'buildCommand' => 'bash prepare.sh && bash build.sh', + 'buildCommand' => 'bash build.sh', ]), ], 'vcsProvider' => 'github', @@ -1351,4 +1351,23 @@ return [ 'providerVersion' => '0.3.*', 'variables' => [], ], + [ + 'key' => 'gallery-for-lynx', + 'name' => 'Lynx gallery', + 'tagline' => 'A Lynx website showcasing gallery with smooth animations.', + 'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible) + 'useCases' => [UseCases::STARTER], + 'screenshotDark' => $url . '/images/sites/templates/gallery-for-lynx-dark.png', + 'screenshotLight' => $url . '/images/sites/templates/gallery-for-lynx-light.png', + 'frameworks' => [ + getFramework('LYNX', [ + 'providerRootDirectory' => './lynx/gallery', + ]), + ], + 'vcsProvider' => 'github', + 'providerRepositoryId' => 'templates-for-sites', + 'providerOwner' => 'appwrite', + 'providerVersion' => '0.3.*', + 'variables' => [] + ], ]; diff --git a/app/config/variables.php b/app/config/variables.php index d6b8069f13..20d37be0f3 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -106,6 +106,15 @@ return [ 'question' => '', 'filter' => '' ], + [ + 'name' => '_APP_DOMAIN_SITES', + 'description' => 'A domain to use for site preview URLs.', + 'introduction' => '', + 'default' => 'sites.localhost', + 'required' => false, + 'question' => '', + 'filter' => '' + ], [ 'name' => '_APP_DOMAIN_TARGET', 'description' => 'Deprecated since 1.7.0. A DNS A record hostname to serve as a CNAME target for your Appwrite custom domains. You can use the same value as used for the Appwrite \'_APP_DOMAIN\' variable. The default value is \'localhost\'.', diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index ac01476314..0c53423325 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -190,7 +190,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res [ '$id' => ID::unique(), 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'provider' => Auth::getSessionProviderByTokenType($verifiedToken->getAttribute('type')), 'secret' => Auth::hash($sessionSecret), // One way hash encryption to protect DB leak 'userAgent' => $request->getUserAgent('UNKNOWN'), @@ -387,7 +387,7 @@ App::post('/v1/account') 'search' => implode(' ', [$userId, $email, $name]), 'accessedAt' => DateTime::now(), ]); - $user->removeAttribute('$internalId'); + $user->removeAttribute('$sequence'); $user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); try { $target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([ @@ -397,7 +397,7 @@ App::post('/v1/account') Permission::delete(Role::user($user->getId())), ], 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'providerType' => MESSAGE_TYPE_EMAIL, 'identifier' => $email, ]))); @@ -907,7 +907,7 @@ App::post('/v1/account/sessions/email') [ '$id' => ID::unique(), 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'provider' => Auth::SESSION_PROVIDER_EMAIL, 'providerUid' => $email, 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak @@ -1056,7 +1056,7 @@ App::post('/v1/account/sessions/anonymous') 'search' => $userId, 'accessedAt' => DateTime::now(), ]); - $user->removeAttribute('$internalId'); + $user->removeAttribute('$sequence'); Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); // Create session token @@ -1069,7 +1069,7 @@ App::post('/v1/account/sessions/anonymous') [ '$id' => ID::unique(), 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'provider' => Auth::SESSION_PROVIDER_ANONYMOUS, 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'userAgent' => $request->getUserAgent('UNKNOWN'), @@ -1440,7 +1440,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $identityWithMatchingEmail = $dbForProject->findOne('identities', [ Query::equal('providerEmail', [$email]), - Query::notEqual('userInternalId', $user->getInternalId()), + Query::notEqual('userInternalId', $user->getSequence()), ]); if (!$identityWithMatchingEmail->isEmpty()) { $failureRedirect(Exception::USER_ALREADY_EXISTS); @@ -1554,7 +1554,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'search' => implode(' ', [$userId, $email, $name]), 'accessedAt' => DateTime::now(), ]); - $user->removeAttribute('$internalId'); + $user->removeAttribute('$sequence'); $userDoc = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); $dbForProject->createDocument('targets', new Document([ '$permissions' => [ @@ -1563,7 +1563,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') Permission::delete(Role::user($user->getId())), ], 'userId' => $userDoc->getId(), - 'userInternalId' => $userDoc->getInternalId(), + 'userInternalId' => $userDoc->getSequence(), 'providerType' => MESSAGE_TYPE_EMAIL, 'identifier' => $email, ])); @@ -1582,7 +1582,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') } $identity = $dbForProject->findOne('identities', [ - Query::equal('userInternalId', [$user->getInternalId()]), + Query::equal('userInternalId', [$user->getSequence()]), Query::equal('provider', [$provider]), Query::equal('providerUid', [$oauth2ID]), ]); @@ -1592,7 +1592,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $identitiesWithMatchingEmail = $dbForProject->find('identities', [ Query::equal('providerEmail', [$email]), - Query::notEqual('userInternalId', $user->getInternalId()), + Query::notEqual('userInternalId', $user->getSequence()), ]); if (!empty($identitiesWithMatchingEmail)) { $failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */ @@ -1605,7 +1605,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') Permission::update(Role::user($userId)), Permission::delete(Role::user($userId)), ], - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'userId' => $userId, 'provider' => $provider, 'providerUid' => $oauth2ID, @@ -1648,7 +1648,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $token = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'type' => Auth::TOKEN_TYPE_OAUTH2, 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'expire' => $expire, @@ -1683,7 +1683,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $session = new Document(array_merge([ '$id' => ID::unique(), 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'provider' => $provider, 'providerUid' => $oauth2ID, 'providerAccessToken' => $accessToken, @@ -1736,7 +1736,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $target ->setAttribute('sessionId', $session->getId()) - ->setAttribute('sessionInternalId', $session->getInternalId()); + ->setAttribute('sessionInternalId', $session->getSequence()); $dbForProject->updateDocument('targets', $target->getId(), $target); } @@ -1931,7 +1931,7 @@ App::post('/v1/account/tokens/magic-url') 'accessedAt' => DateTime::now(), ]); - $user->removeAttribute('$internalId'); + $user->removeAttribute('$sequence'); Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); } @@ -1941,7 +1941,7 @@ App::post('/v1/account/tokens/magic-url') $token = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'type' => Auth::TOKEN_TYPE_MAGIC_URL, 'secret' => Auth::hash($tokenSecret), // One way hash encryption to protect DB leak 'expire' => $expire, @@ -2168,7 +2168,7 @@ App::post('/v1/account/tokens/email') 'accessedAt' => DateTime::now(), ]); - $user->removeAttribute('$internalId'); + $user->removeAttribute('$sequence'); Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); } @@ -2178,7 +2178,7 @@ App::post('/v1/account/tokens/email') $token = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'type' => Auth::TOKEN_TYPE_EMAIL, 'secret' => Auth::hash($tokenSecret), // One way hash encryption to protect DB leak 'expire' => $expire, @@ -2460,7 +2460,7 @@ App::post('/v1/account/tokens/phone') 'accessedAt' => DateTime::now(), ]); - $user->removeAttribute('$internalId'); + $user->removeAttribute('$sequence'); Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); try { $target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([ @@ -2470,7 +2470,7 @@ App::post('/v1/account/tokens/phone') Permission::delete(Role::user($user->getId())), ], 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'providerType' => MESSAGE_TYPE_SMS, 'identifier' => $phone, ]))); @@ -2501,7 +2501,7 @@ App::post('/v1/account/tokens/phone') $token = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'type' => Auth::TOKEN_TYPE_PHONE, 'secret' => Auth::hash($secret), 'expire' => $expire, @@ -2703,7 +2703,7 @@ App::get('/v1/account/logs') $audit = new EventAudit($dbForProject); - $logs = $audit->getLogsByUser($user->getInternalId(), $queries); + $logs = $audit->getLogsByUser($user->getSequence(), $queries); $output = []; @@ -2732,7 +2732,7 @@ App::get('/v1/account/logs') } $response->dynamic(new Document([ - 'total' => $audit->countLogsByUser($user->getInternalId(), $queries), + 'total' => $audit->countLogsByUser($user->getSequence(), $queries), 'logs' => $output, ]), Response::MODEL_LOG_LIST); }); @@ -2900,7 +2900,7 @@ App::patch('/v1/account/email') // Makes sure this email is not already used in another identity $identityWithMatchingEmail = $dbForProject->findOne('identities', [ Query::equal('providerEmail', [$email]), - Query::notEqual('userInternalId', $user->getInternalId()), + Query::notEqual('userInternalId', $user->getSequence()), ]); if (!$identityWithMatchingEmail->isEmpty()) { throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */ @@ -3183,7 +3183,7 @@ App::post('/v1/account/recovery') $recovery = new Document([ '$id' => ID::unique(), 'userId' => $profile->getId(), - 'userInternalId' => $profile->getInternalId(), + 'userInternalId' => $profile->getSequence(), 'type' => Auth::TOKEN_TYPE_RECOVERY, 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'expire' => $expire, @@ -3438,7 +3438,7 @@ App::post('/v1/account/verification') $verification = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'type' => Auth::TOKEN_TYPE_VERIFICATION, 'secret' => Auth::hash($verificationSecret), // One way hash encryption to protect DB leak 'expire' => $expire, @@ -3685,7 +3685,7 @@ App::post('/v1/account/verification/phone') $verification = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'type' => Auth::TOKEN_TYPE_PHONE, 'secret' => Auth::hash($secret), 'expire' => $expire, @@ -3979,7 +3979,7 @@ App::post('/v1/account/mfa/authenticators/:type') $authenticator = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'type' => Type::TOTP, 'verified' => false, 'data' => [ @@ -4292,7 +4292,7 @@ App::post('/v1/account/mfa/challenge') $code = Auth::codeGenerator(); $challenge = new Document([ 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'type' => $factor, 'token' => Auth::tokenGenerator(), 'code' => $code, @@ -4621,12 +4621,12 @@ App::post('/v1/account/targets/push') Permission::delete(Role::user($user->getId())), ], 'providerId' => !empty($providerId) ? $providerId : null, - 'providerInternalId' => !empty($providerId) ? $provider->getInternalId() : null, + 'providerInternalId' => !empty($providerId) ? $provider->getSequence() : null, 'providerType' => MESSAGE_TYPE_PUSH, 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'sessionId' => $session->getId(), - 'sessionInternalId' => $session->getInternalId(), + 'sessionInternalId' => $session->getSequence(), 'identifier' => $identifier, 'name' => "{$device['deviceBrand']} {$device['deviceModel']}" ])); @@ -4745,7 +4745,7 @@ App::delete('/v1/account/targets/:targetId/push') throw new Exception(Exception::USER_TARGET_NOT_FOUND); } - if ($user->getInternalId() !== $target->getAttribute('userInternalId')) { + if ($user->getSequence() !== $target->getAttribute('userInternalId')) { throw new Exception(Exception::USER_TARGET_NOT_FOUND); } @@ -4794,7 +4794,7 @@ App::get('/v1/account/identities') throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - $queries[] = Query::equal('userInternalId', [$user->getInternalId()]); + $queries[] = Query::equal('userInternalId', [$user->getSequence()]); /** * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 779a188089..3a7b4aa582 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -669,7 +669,7 @@ App::get('/v1/cards/cloud') } } - $isPlatinum = $user->getInternalId() % 100 === 0; + $isPlatinum = $user->getSequence() % 100 === 0; } else { $name = $mock === 'normal-long' ? 'Sir First Walter O\'Brian Junior' : 'Walter O\'Brian'; $createdAt = new \DateTime('now'); @@ -859,7 +859,7 @@ App::get('/v1/cards/cloud-back') $isEmployee = \array_key_exists($email, $employees); $isGolden = $isEmployee || $isHero || $isContributor; - $isPlatinum = $user->getInternalId() % 100 === 0; + $isPlatinum = $user->getSequence() % 100 === 0; } else { $userId = '63e0bcf3c3eb803ba530'; @@ -926,9 +926,9 @@ App::get('/v1/cards/cloud-og') } if (!$mock) { - $internalId = $user->getInternalId(); - $bgVariation = $internalId % 3 === 0 ? '1' : ($internalId % 3 === 1 ? '2' : '3'); - $cardVariation = $internalId % 3 === 0 ? '1' : ($internalId % 3 === 1 ? '2' : '3'); + $sequence = $user->getSequence(); + $bgVariation = $sequence % 3 === 0 ? '1' : ($sequence % 3 === 1 ? '2' : '3'); + $cardVariation = $sequence % 3 === 0 ? '1' : ($sequence % 3 === 1 ? '2' : '3'); $name = $user->getAttribute('name', 'Anonymous'); $email = $user->getAttribute('email', ''); @@ -958,7 +958,7 @@ App::get('/v1/cards/cloud-og') } } - $isPlatinum = $user->getInternalId() % 100 === 0; + $isPlatinum = $user->getSequence() % 100 === 0; } else { $bgVariation = \str_ends_with($mock, '-bg2') ? '2' : (\str_ends_with($mock, '-bg3') ? '3' : '1'); $cardVariation = \str_ends_with($mock, '-right') ? '2' : (\str_ends_with($mock, '-middle') ? '3' : '1'); diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 62ddd41daa..0e85171772 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -38,6 +38,7 @@ use Utopia\Database\Exception\Relationship as RelationshipException; use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Exception\Structure as StructureException; use Utopia\Database\Exception\Truncate as TruncateException; +use Utopia\Database\Exception\Type as TypeException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -62,6 +63,7 @@ use Utopia\Validator\Integer; use Utopia\Validator\IP; use Utopia\Validator\JSON; use Utopia\Validator\Nullable; +use Utopia\Validator\Numeric; use Utopia\Validator\Range; use Utopia\Validator\Text; use Utopia\Validator\URL; @@ -107,7 +109,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -130,7 +132,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att if ($type === Database::VAR_RELATIONSHIP) { $options['side'] = Database::RELATION_SIDE_PARENT; - $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection'] ?? ''); + $relatedCollection = $dbForProject->getDocument('database_' . $database->getSequence(), $options['relatedCollection'] ?? ''); if ($relatedCollection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND, 'The related collection was not found.'); } @@ -138,11 +140,11 @@ function createAttribute(string $databaseId, string $collectionId, Document $att try { $attribute = new Document([ - '$id' => ID::custom($database->getInternalId() . '_' . $collection->getInternalId() . '_' . $key), + '$id' => ID::custom($database->getSequence() . '_' . $collection->getSequence() . '_' . $key), 'key' => $key, - 'databaseInternalId' => $database->getInternalId(), + 'databaseInternalId' => $database->getSequence(), 'databaseId' => $database->getId(), - 'collectionInternalId' => $collection->getInternalId(), + 'collectionInternalId' => $collection->getSequence(), 'collectionId' => $collectionId, 'type' => $type, 'status' => 'processing', // processing, available, failed, deleting, stuck @@ -164,13 +166,13 @@ function createAttribute(string $databaseId, string $collectionId, Document $att } catch (LimitException) { throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED); } catch (\Throwable $e) { - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); - $dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $collectionId); + $dbForProject->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence()); throw $e; } - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); - $dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $collectionId); + $dbForProject->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence()); if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) { $twoWayKey = $options['twoWayKey']; @@ -181,11 +183,11 @@ function createAttribute(string $databaseId, string $collectionId, Document $att try { try { $twoWayAttribute = new Document([ - '$id' => ID::custom($database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $twoWayKey), + '$id' => ID::custom($database->getSequence() . '_' . $relatedCollection->getSequence() . '_' . $twoWayKey), 'key' => $twoWayKey, - 'databaseInternalId' => $database->getInternalId(), + 'databaseInternalId' => $database->getSequence(), 'databaseId' => $database->getId(), - 'collectionInternalId' => $relatedCollection->getInternalId(), + 'collectionInternalId' => $relatedCollection->getSequence(), 'collectionId' => $relatedCollection->getId(), 'type' => $type, 'status' => 'processing', // processing, available, failed, deleting, stuck @@ -213,13 +215,13 @@ function createAttribute(string $databaseId, string $collectionId, Document $att $dbForProject->deleteDocument('attributes', $attribute->getId()); throw $e; } finally { - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); - $dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $collectionId); + $dbForProject->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence()); } // If operation succeeded, purge the cache for the related collection too - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); - $dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $relatedCollection->getId()); + $dbForProject->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence()); } $queueForDatabase @@ -262,12 +264,12 @@ function updateAttribute( throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $attribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); + $attribute = $dbForProject->getDocument('attributes', $database->getSequence() . '_' . $collection->getSequence() . '_' . $key); if ($attribute->isEmpty()) { throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); } @@ -292,7 +294,7 @@ function updateAttribute( throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array attributes'); } - $collectionId = 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(); + $collectionId = 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(); $attribute ->setAttribute('default', $default) @@ -378,8 +380,8 @@ function updateAttribute( } if ($primaryDocumentOptions['twoWay']) { - $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $primaryDocumentOptions['relatedCollection']); - $relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey']); + $relatedCollection = $dbForProject->getDocument('database_' . $database->getSequence(), $primaryDocumentOptions['relatedCollection']); + $relatedAttribute = $dbForProject->getDocument('attributes', $database->getSequence() . '_' . $relatedCollection->getSequence() . '_' . $primaryDocumentOptions['twoWayKey']); if (!empty($newKey) && $newKey !== $key) { $options['twoWayKey'] = $newKey; @@ -389,8 +391,8 @@ function updateAttribute( $relatedAttribute->setAttribute('options', $relatedOptions); - $dbForProject->updateDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $primaryDocumentOptions['twoWayKey'], $relatedAttribute); - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); + $dbForProject->updateDocument('attributes', $database->getSequence() . '_' . $relatedCollection->getSequence() . '_' . $primaryDocumentOptions['twoWayKey'], $relatedAttribute); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $relatedCollection->getId()); } } else { try { @@ -418,7 +420,7 @@ function updateAttribute( $originalUid = $attribute->getId(); $attribute - ->setAttribute('$id', ID::custom($database->getInternalId() . '_' . $collection->getInternalId() . '_' . $newKey)) + ->setAttribute('$id', ID::custom($database->getSequence() . '_' . $collection->getSequence() . '_' . $newKey)) ->setAttribute('key', $newKey); try { @@ -444,10 +446,10 @@ function updateAttribute( } } } else { - $attribute = $dbForProject->updateDocument('attributes', $database->getInternalId() . '_' . $collection->getInternalId() . '_' . $key, $attribute); + $attribute = $dbForProject->updateDocument('attributes', $database->getSequence() . '_' . $collection->getSequence() . '_' . $key, $attribute); } - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId()); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $collection->getId()); $queueForEvents ->setContext('collection', $collection) @@ -536,7 +538,7 @@ App::post('/v1/databases') } try { - $dbForProject->createCollection('database_' . $database->getInternalId(), $attributes, $indexes); + $dbForProject->createCollection('database_' . $database->getSequence(), $attributes, $indexes); } catch (DuplicateException) { throw new Exception(Exception::DATABASE_ALREADY_EXISTS); } catch (IndexException) { @@ -839,7 +841,7 @@ App::delete('/v1/databases/:databaseId') } $dbForProject->purgeCachedDocument('databases', $database->getId()); - $dbForProject->purgeCachedCollection('databases_' . $database->getInternalId()); + $dbForProject->purgeCachedCollection('database_' . $database->getSequence()); $queueForDatabase ->setType(DATABASE_TYPE_DELETE_DATABASE) @@ -899,9 +901,9 @@ App::post('/v1/databases/:databaseId/collections') $permissions = Permission::aggregate($permissions) ?? []; try { - $collection = $dbForProject->createDocument('database_' . $database->getInternalId(), new Document([ + $collection = $dbForProject->createDocument('database_' . $database->getSequence(), new Document([ '$id' => $collectionId, - 'databaseInternalId' => $database->getInternalId(), + 'databaseInternalId' => $database->getSequence(), 'databaseId' => $databaseId, '$permissions' => $permissions, 'documentSecurity' => $documentSecurity, @@ -919,7 +921,7 @@ App::post('/v1/databases/:databaseId/collections') try { $dbForProject->createCollection( - id: 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), + id: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), permissions: $permissions, documentSecurity: $documentSecurity ); @@ -1000,7 +1002,7 @@ App::get('/v1/databases/:databaseId/collections') $collectionId = $cursor->getValue(); - $cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $cursorDocument = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Collection '{$collectionId}' for the 'cursor' value not found."); @@ -1009,7 +1011,7 @@ App::get('/v1/databases/:databaseId/collections') $cursor->setValue($cursorDocument); } - $collectionId = 'database_' . $database->getInternalId(); + $collectionId = 'database_' . $database->getSequence(); try { $collections = $dbForProject->find($collectionId, $queries); @@ -1057,7 +1059,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -1100,8 +1102,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); - $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collectionDocument->getInternalId()); + $collectionDocument = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); + $collection = $dbForProject->getCollection('database_' . $database->getSequence() . '_collection_' . $collectionDocument->getSequence()); if ($collectionDocument->isEmpty() || $collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -1215,7 +1217,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -1236,12 +1238,12 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') ->setAttribute('search', \implode(' ', [$collectionId, $name])); $collection = $dbForProject->updateDocument( - 'database_' . $database->getInternalId(), + 'database_' . $database->getSequence(), $collectionId, $collection ); - $dbForProject->updateCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $permissions, $documentSecurity); + $dbForProject->updateCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $permissions, $documentSecurity); $queueForEvents ->setContext('database', $database) @@ -1287,17 +1289,17 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - if (!$dbForProject->deleteDocument('database_' . $database->getInternalId(), $collectionId)) { + if (!$dbForProject->deleteDocument('database_' . $database->getSequence(), $collectionId)) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove collection from DB'); } - $dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + $dbForProject->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence()); $queueForDatabase ->setType(DATABASE_TYPE_DELETE_COLLECTION) @@ -1347,7 +1349,17 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->inject('plan') + ->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, array $plan) { + if (!App::isDevelopment() && $encrypt && !empty($plan) && !($plan['databasesAllowEncrypt'] ?? false)) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Encrypted string attributes are not available on your plan. Please upgrade to create encrypted string attributes.'); + } + if ($encrypt && $size < APP_DATABASE_ENCRYPT_SIZE_MIN) { + throw new Exception( + Exception::GENERAL_BAD_REQUEST, + "Size too small. Encrypted strings require a minimum size of " . APP_DATABASE_ENCRYPT_SIZE_MIN . " characters." + ); + } // Ensure attribute default is within required size $validator = new Text($size, 0); if (!is_null($default) && !$validator->isValid($default)) { @@ -1368,7 +1380,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string 'array' => $array, 'filters' => $filters, ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); - + $attribute->setAttribute('encrypt', $encrypt); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) ->dynamic($attribute, Response::MODEL_ATTRIBUTE_STRING); @@ -1878,15 +1890,15 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); - $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); + $collection = $dbForProject->getCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence()); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $relatedCollectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId); - $relatedCollection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollectionDocument->getInternalId()); + $relatedCollectionDocument = $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId); + $relatedCollection = $dbForProject->getCollection('database_' . $database->getSequence() . '_collection_' . $relatedCollectionDocument->getSequence()); if ($relatedCollection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -1987,7 +1999,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -2000,8 +2012,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') \array_push( $queries, - Query::equal('databaseInternalId', [$database->getInternalId()]), - Query::equal('collectionInternalId', [$collection->getInternalId()]), + Query::equal('databaseInternalId', [$database->getSequence()]), + Query::equal('collectionInternalId', [$collection->getSequence()]), ); /** @@ -2023,8 +2035,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') try { $cursorDocument = $dbForProject->findOne('attributes', [ - Query::equal('databaseInternalId', [$database->getInternalId()]), - Query::equal('collectionInternalId', [$collection->getInternalId()]), + Query::equal('databaseInternalId', [$database->getSequence()]), + Query::equal('collectionInternalId', [$collection->getSequence()]), Query::equal('key', [$attributeId]), ]); } catch (QueryException $e) { @@ -2047,6 +2059,13 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') throw new Exception(Exception::GENERAL_QUERY_INVALID); } + foreach ($attributes as $attribute) { + if ($attribute->getAttribute('type') === Database::VAR_STRING) { + $filters = $attribute->getAttribute('filters', []); + $attribute->setAttribute('encrypt', in_array('encrypt', $filters)); + } + } + $response->dynamic(new Document([ 'attributes' => $attributes, 'total' => $total, @@ -2095,13 +2114,13 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $attribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); + $attribute = $dbForProject->getDocument('attributes', $database->getSequence() . '_' . $collection->getSequence() . '_' . $key); if ($attribute->isEmpty()) { throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); @@ -2111,7 +2130,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') $type = $attribute->getAttribute('type'); $format = $attribute->getAttribute('format'); $options = $attribute->getAttribute('options', []); - + $filters = $attribute->getAttribute('filters', []); foreach ($options as $key => $option) { $attribute->setAttribute($key, $option); } @@ -2131,7 +2150,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') }, default => Response::MODEL_ATTRIBUTE, }; - + $attribute->setAttribute('encrypt', in_array('encrypt', $filters)); $response->dynamic($attribute, $model); }); @@ -2708,13 +2727,13 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $attribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); + $attribute = $dbForProject->getDocument('attributes', $database->getSequence() . '_' . $collection->getSequence() . '_' . $key); if ($attribute->isEmpty()) { throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); @@ -2737,19 +2756,19 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key $attribute = $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'deleting')); } - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); - $dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $collectionId); + $dbForProject->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence()); if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { $options = $attribute->getAttribute('options'); if ($options['twoWay']) { - $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); + $relatedCollection = $dbForProject->getDocument('database_' . $database->getSequence(), $options['relatedCollection']); if ($relatedCollection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); + $relatedAttribute = $dbForProject->getDocument('attributes', $database->getSequence() . '_' . $relatedCollection->getSequence() . '_' . $options['twoWayKey']); if ($relatedAttribute->isEmpty()) { throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); @@ -2759,8 +2778,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'deleting')); } - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $options['relatedCollection']); - $dbForProject->purgeCachedCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId()); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $options['relatedCollection']); + $dbForProject->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence()); } } @@ -2830,7 +2849,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE]), 'Index type.') ->param('attributes', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of attributes to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' attributes are allowed, each 32 characters long.') ->param('orders', [], new ArrayList(new WhiteList(['ASC', 'DESC'], false, Database::VAR_STRING), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index orders. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' orders are allowed.', true) - ->param('lengths', [], new ArrayList(new Nullable(new Integer()), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Length of index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE, optional:true) + ->param('lengths', [], new ArrayList(new Nullable(new Integer()), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Length of index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE, optional: true) ->inject('response') ->inject('dbForProject') ->inject('queueForDatabase') @@ -2842,7 +2861,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -2851,8 +2870,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') $limit = $dbForProject->getLimitForIndexes(); $count = $dbForProject->count('indexes', [ - Query::equal('collectionInternalId', [$collection->getInternalId()]), - Query::equal('databaseInternalId', [$database->getInternalId()]) + Query::equal('collectionInternalId', [$collection->getSequence()]), + Query::equal('databaseInternalId', [$database->getSequence()]) ], max: $limit); @@ -2922,12 +2941,12 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') } $index = new Document([ - '$id' => ID::custom($database->getInternalId() . '_' . $collection->getInternalId() . '_' . $key), + '$id' => ID::custom($database->getSequence() . '_' . $collection->getSequence() . '_' . $key), 'key' => $key, 'status' => 'processing', // processing, available, failed, deleting, stuck - 'databaseInternalId' => $database->getInternalId(), + 'databaseInternalId' => $database->getSequence(), 'databaseId' => $databaseId, - 'collectionInternalId' => $collection->getInternalId(), + 'collectionInternalId' => $collection->getSequence(), 'collectionId' => $collectionId, 'type' => $type, 'attributes' => $attributes, @@ -2951,7 +2970,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') throw new Exception(Exception::INDEX_ALREADY_EXISTS); } - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $collectionId); $queueForDatabase ->setType(DATABASE_TYPE_CREATE_INDEX) @@ -3003,7 +3022,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -3034,8 +3053,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') $indexId = $cursor->getValue(); $cursorDocument = Authorization::skip(fn () => $dbForProject->find('indexes', [ - Query::equal('collectionInternalId', [$collection->getInternalId()]), - Query::equal('databaseInternalId', [$database->getInternalId()]), + Query::equal('collectionInternalId', [$collection->getSequence()]), + Query::equal('databaseInternalId', [$database->getSequence()]), Query::equal('key', [$indexId]), Query::limit(1) ])); @@ -3094,7 +3113,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -3109,7 +3128,6 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') $response->dynamic($index, Response::MODEL_INDEX); }); - App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->alias('/v1/database/collections/:collectionId/indexes/:key') ->desc('Delete index') @@ -3147,13 +3165,13 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $index = $dbForProject->getDocument('indexes', $database->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); + $index = $dbForProject->getDocument('indexes', $database->getSequence() . '_' . $collection->getSequence() . '_' . $key); if ($index->isEmpty()) { throw new Exception(Exception::INDEX_NOT_FOUND); @@ -3164,7 +3182,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') $index = $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'deleting')); } - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $collectionId); $queueForDatabase ->setType(DATABASE_TYPE_DELETE_INDEX) @@ -3203,7 +3221,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') group: 'documents', name: 'createDocument', description: '/docs/references/databases/create-document.md', - auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], + auth: [AuthType::ADMIN, AuthType::SESSION, AuthType::KEY, AuthType::JWT], responses: [ new SDKResponse( code: Response::STATUS_CODE_CREATED, @@ -3224,7 +3242,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') group: 'documents', name: 'createDocuments', description: '/docs/references/databases/create-documents.md', - auth: [AuthType::KEY], + auth: [AuthType::ADMIN, AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_CREATED, @@ -3300,7 +3318,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -3409,7 +3427,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $relatedCollectionId = $relationship->getAttribute('relatedCollection'); $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) ); foreach ($relations as &$relation) { @@ -3423,7 +3441,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') } if ($relation instanceof Document) { $current = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), $relation->getId()) + fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(), $relation->getId()) ); if ($current->isEmpty()) { @@ -3478,7 +3496,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') try { $dbForProject->createDocuments( - 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documents ); } catch (DuplicateException) { @@ -3520,7 +3538,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $relatedCollectionId = $relationship->getAttribute('relatedCollection'); $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) ); foreach ($related as $relation) { @@ -3537,7 +3555,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $operations)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); // per collection + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); // per collection $response->setStatusCode(Response::STATUS_CODE_CREATED); @@ -3595,7 +3613,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -3623,7 +3641,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $documentId = $cursor->getValue(); - $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Document '{$documentId}' for the 'cursor' value not found."); @@ -3632,8 +3650,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $cursor->setValue($cursorDocument); } try { - $documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries); - $total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT); + $documents = $dbForProject->find('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $queries); + $total = $dbForProject->count('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $queries, APP_LIMIT_COUNT); } catch (OrderException $e) { throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null."); } catch (QueryException $e) { @@ -3677,7 +3695,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $relatedCollectionId = $relationship->getAttribute('relatedCollection'); // todo: Use local cache for this getDocument - $relatedCollection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId)); + $relatedCollection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId)); foreach ($relations as $index => $doc) { if ($doc instanceof Document) { @@ -3703,7 +3721,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_READS, \max(1, $operations)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), \max(1, $operations)); + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_READS), \max(1, $operations)); $select = \array_reduce($queries, function ($result, $query) { return $result || ($query->getMethod() === Query::TYPE_SELECT); @@ -3774,7 +3792,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -3785,7 +3803,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - $document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId, $queries); + try { + $document = $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId, $queries); + } catch (QueryException $e) { + throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); + } + if ($document->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); } @@ -3825,7 +3848,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $relatedCollectionId = $relationship->getAttribute('relatedCollection'); $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) ); foreach ($related as $relation) { @@ -3840,7 +3863,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_READS, \max(1, $operations)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), \max(1, $operations)); + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_READS), \max(1, $operations)); $response->dynamic($document, Response::MODEL_DOCUMENT); }); @@ -3879,12 +3902,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } - $document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId); + $document = $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId); if ($document->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); @@ -4008,13 +4031,13 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } // Read permission should not be required for update - $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId)); if ($document->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); } @@ -4083,7 +4106,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $relatedCollectionId = $relationship->getAttribute('relatedCollection'); $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) ); foreach ($relations as &$relation) { @@ -4098,7 +4121,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum } if ($relation instanceof Document) { $oldDocument = Authorization::skip(fn () => $dbForProject->getDocument( - 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), + 'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(), $relation->getId() )); $relation->removeAttribute('$collectionId'); @@ -4106,7 +4129,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum // Attribute $collection is required for Utopia. $relation->setAttribute( '$collection', - 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId() + 'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence() ); if ($oldDocument->isEmpty()) { @@ -4130,11 +4153,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $operations)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); try { $document = $dbForProject->updateDocument( - 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $document->getId(), $newDocument ); @@ -4170,7 +4193,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $relatedCollectionId = $relationship->getAttribute('relatedCollection'); $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) ); foreach ($related as $relation) { @@ -4202,6 +4225,398 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $response->dynamic($document, Response::MODEL_DOCUMENT); }); +App::put('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') + ->desc('Upsert document') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].upsert') + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'document.upsert') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{response.$id}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', new Method( + namespace: 'databases', + group: 'documents', + name: 'upsertDocument', + description: '/docs/references/databases/upsert-document.md', + auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_DOCUMENT, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('documentId', '', new CustomId(), 'Document ID.') + ->param('data', [], new JSON(), 'Document data as JSON object. Include all required attributes of the document to be created or updated.') + ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->inject('requestTimestamp') + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array + + if (empty($data) && \is_null($permissions)) { + throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD); + } + + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + // Map aggregate permissions into the multiple permissions they represent. + $permissions = Permission::aggregate($permissions, [ + Database::PERMISSION_READ, + Database::PERMISSION_UPDATE, + Database::PERMISSION_DELETE, + ]); + + // Users can only manage their own roles, API keys and Admin users can manage any + $roles = Authorization::getRoles(); + if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) { + foreach (Database::PERMISSIONS as $type) { + foreach ($permissions as $permission) { + $permission = Permission::parse($permission); + if ($permission->getPermission() != $type) { + continue; + } + $role = (new Role( + $permission->getRole(), + $permission->getIdentifier(), + $permission->getDimension() + ))->toString(); + if (!Authorization::isRole($role)) { + throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); + } + } + } + } + + $data['$id'] = $documentId; + $data['$permissions'] = $permissions; + $newDocument = new Document($data); + + $operations = 0; + + $setCollection = (function (Document $collection, Document $document) use (&$setCollection, $dbForProject, $database, &$operations) { + + $operations++; + + $relationships = \array_filter( + $collection->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $document->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + continue; + } + + $isList = \is_array($related) && \array_values($related) === $related; + + if ($isList) { + $relations = $related; + } else { + $relations = [$related]; + } + + $relatedCollectionId = $relationship->getAttribute('relatedCollection'); + $relatedCollection = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) + ); + + foreach ($relations as &$relation) { + // If the relation is an array it can be either update or create a child document. + if ( + \is_array($relation) + && \array_values($relation) !== $relation + && !isset($relation['$id']) + ) { + $relation['$id'] = ID::unique(); + $relation = new Document($relation); + } + if ($relation instanceof Document) { + $oldDocument = Authorization::skip(fn () => $dbForProject->getDocument( + 'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(), + $relation->getId() + )); + $relation->removeAttribute('$collectionId'); + $relation->removeAttribute('$databaseId'); + // Attribute $collection is required for Utopia. + $relation->setAttribute( + '$collection', + 'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence() + ); + + if ($oldDocument->isEmpty()) { + if (isset($relation['$id']) && $relation['$id'] === 'unique()') { + $relation['$id'] = ID::unique(); + } + } + $setCollection($relatedCollection, $relation); + } + } + + if ($isList) { + $document->setAttribute($relationship->getAttribute('key'), \array_values($relations)); + } else { + $document->setAttribute($relationship->getAttribute('key'), \reset($relations)); + } + } + }); + + $setCollection($collection, $newDocument); + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $operations)) + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); + + $upserted = []; + try { + $modified = $dbForProject->createOrUpdateDocuments( + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + [$newDocument], + onNext: function (Document $document) use (&$upserted) { + $upserted[] = $document; + }, + ); + } catch (ConflictException) { + throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); + } catch (DuplicateException) { + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); + } catch (RelationshipException $e) { + throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage()); + } catch (StructureException $e) { + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $e->getMessage()); + } + + $document = $upserted[0]; + // Add $collectionId and $databaseId for all documents + $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { + $document->setAttribute('$databaseId', $database->getId()); + $document->setAttribute('$collectionId', $collection->getId()); + + $relationships = \array_filter( + $collection->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ); + + foreach ($relationships as $relationship) { + $related = $document->getAttribute($relationship->getAttribute('key')); + + if (empty($related)) { + continue; + } + if (!\is_array($related)) { + $related = [$related]; + } + + $relatedCollectionId = $relationship->getAttribute('relatedCollection'); + $relatedCollection = Authorization::skip( + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) + ); + + foreach ($related as $relation) { + if ($relation instanceof Document) { + $processDocument($relatedCollection, $relation); + } + } + } + }; + + $processDocument($collection, $document); + + $relationships = \array_map( + fn ($document) => $document->getAttribute('key'), + \array_filter( + $collection->getAttribute('attributes', []), + fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP + ) + ); + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('collectionId', $collection->getId()) + ->setParam('documentId', $document->getId()) + ->setContext('collection', $collection) + ->setContext('database', $database) + ->setPayload($response->getPayload(), sensitive: $relationships); + + $response->dynamic($document, Response::MODEL_DOCUMENT); + }); + +App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/:attribute/increment') + ->desc('Increment document attribute') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].increment') + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'documents.increment') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', new Method( + namespace: 'databases', + group: 'documents', + name: 'incrementDocumentAttribute', + description: '/docs/references/databases/increment-document-attribute.md', + auth: [AuthType::ADMIN, AuthType::KEY, AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_DOCUMENT, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('documentId', '', new UID(), 'Document ID.') + ->param('attribute', '', new Key(), 'Attribute key.') + ->param('value', 1, new Numeric(), 'Value to increment the attribute by. The value must be a number.', true) + ->param('max', null, new Numeric(), 'Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->action(function (string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + if ($collection->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + try { + $document = $dbForProject->increaseDocumentAttribute( + collection: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + id: $documentId, + attribute: $attribute, + value: $value, + max: $max + ); + } catch (ConflictException) { + throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); + } catch (NotFoundException) { + throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + } catch (LimitException) { + throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute "' . $attribute . '" has reached the maximum value of ' . $max); + } catch (TypeException) { + throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, 'Attribute "' . $attribute . '" is not a number'); + } + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1) + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('collectionId', $collectionId) + ->setContext('collection', $collection) + ->setContext('database', $database); + + $response->dynamic($document, Response::MODEL_DOCUMENT); + }); + +App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/:attribute/decrement') + ->desc('Decrement document attribute') + ->groups(['api', 'database']) + ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].decrement') + ->label('scope', 'documents.write') + ->label('resourceType', RESOURCE_TYPE_DATABASES) + ->label('audits.event', 'documents.decrement') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') + ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') + ->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2) + ->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT) + ->label('sdk', new Method( + namespace: 'databases', + group: 'documents', + name: 'decrementDocumentAttribute', + description: '/docs/references/databases/decrement-document-attribute.md', + auth: [AuthType::ADMIN, AuthType::KEY, AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_DOCUMENT, + ) + ], + contentType: ContentType::JSON + )) + ->param('databaseId', '', new UID(), 'Database ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('documentId', '', new UID(), 'Document ID.') + ->param('attribute', '', new Key(), 'Attribute key.') + ->param('value', 1, new Numeric(), 'Value to decrement the attribute by. The value must be a number.', true) + ->param('min', null, new Numeric(), 'Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.', true) + ->inject('response') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('queueForStatsUsage') + ->action(function (string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + if ($database->isEmpty()) { + throw new Exception(Exception::DATABASE_NOT_FOUND); + } + + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); + if ($collection->isEmpty()) { + throw new Exception(Exception::COLLECTION_NOT_FOUND); + } + + try { + $document = $dbForProject->decreaseDocumentAttribute( + collection: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + id: $documentId, + attribute: $attribute, + value: $value, + min: $min + ); + } catch (ConflictException) { + throw new Exception(Exception::DOCUMENT_UPDATE_CONFLICT); + } catch (NotFoundException) { + throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); + } catch (LimitException) { + throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute "' . $attribute . '" has reached the minimum value of ' . $min); + } catch (TypeException) { + throw new Exception(Exception::ATTRIBUTE_TYPE_INVALID, 'Attribute "' . $attribute . '" is not a number'); + } + + $queueForStatsUsage + ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1) + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); + + $queueForEvents + ->setParam('databaseId', $databaseId) + ->setParam('collectionId', $collectionId) + ->setContext('collection', $collection) + ->setContext('database', $database); + + $response->dynamic($document, Response::MODEL_DOCUMENT); + }); + App::patch('/v1/databases/:databaseId/collections/:collectionId/documents') ->desc('Update documents') ->groups(['api', 'database']) @@ -4217,7 +4632,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents') group: 'documents', name: 'updateDocuments', description: '/docs/references/databases/update-documents.md', - auth: [AuthType::KEY], + auth: [AuthType::ADMIN, AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -4249,7 +4664,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -4280,7 +4695,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents') try { $modified = $dbForProject->updateDocuments( - 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), new Document($data), $queries, onNext: function (Document $document) use ($plan, &$documents) { @@ -4304,7 +4719,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents') $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $modified)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $modified)); + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $modified)); $response->dynamic(new Document([ 'total' => $modified, @@ -4327,7 +4742,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents') group: 'documents', name: 'upsertDocuments', description: '/docs/references/databases/upsert-documents.md', - auth: [AuthType::KEY], + auth: [AuthType::ADMIN, AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -4338,7 +4753,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents') )) ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID.') - ->param('documents', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of document data as JSON objects. May contain partial documents.', true, ['plan']) + ->param('documents', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of document data as JSON objects. May contain partial documents.', false, ['plan']) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') @@ -4349,7 +4764,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -4371,7 +4786,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents') try { $modified = $dbForProject->createOrUpdateDocuments( - 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documents, onNext: function (Document $document) use ($plan, &$upserted) { if (\count($upserted) < ($plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH)) { @@ -4396,7 +4811,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents') $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $modified)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $modified)); + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $modified)); $response->dynamic(new Document([ 'total' => $modified, @@ -4447,20 +4862,20 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } // Read permission should not be required for delete - $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId)); if ($document->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); } try { $dbForProject->deleteDocument( - 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId ); } catch (ConflictException) { @@ -4495,7 +4910,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $relatedCollectionId = $relationship->getAttribute('relatedCollection'); $relatedCollection = Authorization::skip( - fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) + fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId) ); foreach ($related as $relation) { @@ -4510,7 +4925,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $operations)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $operations)); $relationships = \array_map( fn ($document) => $document->getAttribute('key'), @@ -4546,7 +4961,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents') group: 'documents', name: 'deleteDocuments', description: '/docs/references/databases/delete-documents.md', - auth: [AuthType::KEY], + auth: [AuthType::ADMIN, AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -4569,7 +4984,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); + $collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } @@ -4593,7 +5008,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents') try { $modified = $dbForProject->deleteDocuments( - 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), + 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $queries, onNext: function (Document $document) use ($plan, &$documents) { if (\count($documents) < ($plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH)) { @@ -4614,7 +5029,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents') $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $modified)) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $modified)); + ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $modified)); $response->dynamic(new Document([ 'total' => $modified, @@ -4750,11 +5165,11 @@ App::get('/v1/databases/:databaseId/usage') $stats = $usage = []; $days = $periods[$range]; $metrics = [ - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS), - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS), - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE), - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), - str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES) + str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_COLLECTIONS), + str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_DOCUMENTS), + str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_STORAGE), + str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_READS), + str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES) ]; Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { @@ -4843,8 +5258,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage') ->inject('dbForProject') ->action(function (string $databaseId, string $range, string $collectionId, Response $response, Database $dbForProject) { $database = $dbForProject->getDocument('databases', $databaseId); - $collectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); - $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collectionDocument->getInternalId()); + $collectionDocument = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId); + $collection = $dbForProject->getCollection('database_' . $database->getSequence() . '_collection_' . $collectionDocument->getSequence()); if ($collection->isEmpty()) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -4854,7 +5269,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage') $stats = $usage = []; $days = $periods[$range]; $metrics = [ - str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collectionDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), + str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getSequence(), $collectionDocument->getSequence()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), ]; Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 11fc4cc244..2bdaea3c2c 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -3,13 +3,16 @@ use Appwrite\ClamAV\Network; use Appwrite\Event\Event; use Appwrite\Extend\Exception; +use Appwrite\PubSub\Adapter\Pool as PubSubPool; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\App; +use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Config\Config; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Document; use Utopia\Domains\Validator\PublicDomain; use Utopia\Pools\Group; @@ -34,8 +37,8 @@ App::get('/v1/health') namespace: 'health', group: 'health', name: 'get', - auth: [AuthType::KEY], description: '/docs/references/health/get.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -70,11 +73,11 @@ App::get('/v1/health/db') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'health', name: 'getDB', description: '/docs/references/health/get-db.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -86,8 +89,8 @@ App::get('/v1/health/db') ->inject('response') ->inject('pools') ->action(function (Response $response, Group $pools) { - $output = []; + $failures = []; $configs = [ 'Console.DB' => Config::getParam('pools-console'), @@ -97,7 +100,7 @@ App::get('/v1/health/db') foreach ($configs as $key => $config) { foreach ($config as $database) { try { - $adapter = $pools->get($database)->pop()->getResource(); + $adapter = new DatabasePool($pools->get($database)); $checkStart = \microtime(true); @@ -108,16 +111,16 @@ App::get('/v1/health/db') 'ping' => \round((\microtime(true) - $checkStart) / 1000) ]); } else { - $failure[] = $database; + $failures[] = $database; } - } catch (\Throwable $th) { - $failure[] = $database; + } catch (\Throwable) { + $failures[] = $database; } } } - if (!empty($failure)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'DB failure on: ' . implode(", ", $failure)); + if (!empty($failures)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'DB failure on: ' . implode(", ", $failures)); } $response->dynamic(new Document([ @@ -131,11 +134,11 @@ App::get('/v1/health/cache') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'health', name: 'getCache', description: '/docs/references/health/get-cache.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -147,44 +150,39 @@ App::get('/v1/health/cache') ->inject('response') ->inject('pools') ->action(function (Response $response, Group $pools) { - $output = []; + $failures = []; $configs = [ 'Cache' => Config::getParam('pools-cache'), ]; foreach ($configs as $key => $config) { - foreach ($config as $database) { + foreach ($config as $cache) { try { - /** @var \Utopia\Cache\Adapter $adapter */ - $adapter = $pools->get($database)->pop()->getResource(); + $adapter = new CachePool($pools->get($cache)); $checkStart = \microtime(true); if ($adapter->ping()) { $output[] = new Document([ - 'name' => $key . " ($database)", + 'name' => $key . " ($cache)", 'status' => 'pass', 'ping' => \round((\microtime(true) - $checkStart) / 1000) ]); } else { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'fail', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); + $failures[] = $cache; } - } catch (\Throwable $th) { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'fail', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); + } catch (\Throwable) { + $failures[] = $cache; } } } + if (!empty($failures)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Cache failure on: ' . implode(", ", $failures)); + } + $response->dynamic(new Document([ 'statuses' => $output, 'total' => count($output), @@ -196,11 +194,11 @@ App::get('/v1/health/pubsub') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'health', name: 'getPubSub', description: '/docs/references/health/get-pubsub.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -212,44 +210,39 @@ App::get('/v1/health/pubsub') ->inject('response') ->inject('pools') ->action(function (Response $response, Group $pools) { - $output = []; + $failures = []; $configs = [ 'PubSub' => Config::getParam('pools-pubsub'), ]; foreach ($configs as $key => $config) { - foreach ($config as $database) { + foreach ($config as $pubsub) { try { - /** @var \Appwrite\PubSub\Adapter $adapter */ - $adapter = $pools->get($database)->pop()->getResource(); + $adapter = new PubSubPool($pools->get($pubsub)); $checkStart = \microtime(true); if ($adapter->ping()) { $output[] = new Document([ - 'name' => $key . " ($database)", + 'name' => $key . " ($pubsub)", 'status' => 'pass', 'ping' => \round((\microtime(true) - $checkStart) / 1000) ]); } else { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'fail', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); + $failures[] = $pubsub; } - } catch (\Throwable $th) { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'fail', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); + } catch (\Throwable) { + $failures[] = $pubsub; } } } + if (!empty($failures)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Pubsub failure on: ' . implode(", ", $failures)); + } + $response->dynamic(new Document([ 'statuses' => $output, 'total' => count($output), @@ -261,11 +254,11 @@ App::get('/v1/health/time') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'health', name: 'getTime', description: '/docs/references/health/get-time.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -325,11 +318,11 @@ App::get('/v1/health/queue/webhooks') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueWebhooks', description: '/docs/references/health/get-queue-webhooks.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -351,18 +344,18 @@ App::get('/v1/health/queue/webhooks') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/logs') ->desc('Get logs queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueLogs', description: '/docs/references/health/get-queue-logs.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -384,18 +377,18 @@ App::get('/v1/health/queue/logs') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/certificate') ->desc('Get the SSL certificate for a domain') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'health', name: 'getCertificate', description: '/docs/references/health/get-certificate.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -441,18 +434,18 @@ App::get('/v1/health/certificate') 'validTo' => $certificatePayload['validTo_time_t'], 'signatureTypeSN' => $certificatePayload['signatureTypeSN'], ]), Response::MODEL_HEALTH_CERTIFICATE); - }, ['response']); + }); App::get('/v1/health/queue/certificates') ->desc('Get certificates queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueCertificates', description: '/docs/references/health/get-queue-certificates.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -474,18 +467,18 @@ App::get('/v1/health/queue/certificates') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/builds') ->desc('Get builds queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueBuilds', description: '/docs/references/health/get-queue-builds.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -507,18 +500,18 @@ App::get('/v1/health/queue/builds') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/databases') ->desc('Get databases queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueDatabases', description: '/docs/references/health/get-queue-databases.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -541,18 +534,18 @@ App::get('/v1/health/queue/databases') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/deletes') ->desc('Get deletes queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueDeletes', description: '/docs/references/health/get-queue-deletes.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -574,18 +567,18 @@ App::get('/v1/health/queue/deletes') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/mails') ->desc('Get mails queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueMails', description: '/docs/references/health/get-queue-mails.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -607,18 +600,18 @@ App::get('/v1/health/queue/mails') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/messaging') ->desc('Get messaging queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueMessaging', description: '/docs/references/health/get-queue-messaging.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -640,18 +633,18 @@ App::get('/v1/health/queue/messaging') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/migrations') ->desc('Get migrations queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueMigrations', description: '/docs/references/health/get-queue-migrations.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -673,18 +666,18 @@ App::get('/v1/health/queue/migrations') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/functions') ->desc('Get functions queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueFunctions', description: '/docs/references/health/get-queue-functions.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -706,18 +699,18 @@ App::get('/v1/health/queue/functions') } $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); + }); App::get('/v1/health/queue/stats-resources') ->desc('Get stats resources queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueStatsResources', description: '/docs/references/health/get-queue-stats-resources.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -746,11 +739,11 @@ App::get('/v1/health/queue/stats-usage') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getQueueUsage', description: '/docs/references/health/get-queue-stats-usage.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -779,11 +772,11 @@ App::get('/v1/health/storage/local') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'storage', name: 'getStorageLocal', description: '/docs/references/health/get-storage-local.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -829,11 +822,11 @@ App::get('/v1/health/storage') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'storage', name: 'getStorage', description: '/docs/references/health/get-storage.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -852,15 +845,18 @@ App::get('/v1/health/storage') $checkStart = \microtime(true); foreach ($devices as $device) { - if (!$device->write($device->getPath('health.txt'), 'test', 'text/plain')) { + $uniqueFileName = \uniqid('health', true); + $filePath = $device->getPath($uniqueFileName); + + if (!$device->write($filePath, 'test', 'text/plain')) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed writing test file to ' . $device->getRoot()); } - if ($device->read($device->getPath('health.txt')) !== 'test') { + if ($device->read($filePath) !== 'test') { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed reading test file from ' . $device->getRoot()); } - if (!$device->delete($device->getPath('health.txt'))) { + if (!$device->delete($filePath)) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed deleting test file from ' . $device->getRoot()); } } @@ -878,11 +874,11 @@ App::get('/v1/health/anti-virus') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'health', name: 'getAntivirus', description: '/docs/references/health/get-storage-anti-virus.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -924,11 +920,11 @@ App::get('/v1/health/queue/failed/:name') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( - auth: [AuthType::KEY], namespace: 'health', group: 'queue', name: 'getFailedJobs', description: '/docs/references/health/get-failed-queue-jobs.md', + auth: [AuthType::KEY], responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 1d11e6c392..0bc6f93787 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -2511,11 +2511,11 @@ App::post('/v1/messaging/topics/:topicId/subscribers') Permission::delete(Role::user($user->getId())), ], 'topicId' => $topicId, - 'topicInternalId' => $topic->getInternalId(), + 'topicInternalId' => $topic->getSequence(), 'targetId' => $targetId, - 'targetInternalId' => $target->getInternalId(), + 'targetInternalId' => $target->getSequence(), 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'providerType' => $target->getAttribute('providerType'), 'search' => implode(' ', [ $subscriberId, @@ -2597,7 +2597,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers') throw new Exception(Exception::TOPIC_NOT_FOUND); } - $queries[] = Query::equal('topicInternalId', [$topic->getInternalId()]); + $queries[] = Query::equal('topicInternalId', [$topic->getSequence()]); /** * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries @@ -2947,7 +2947,7 @@ App::post('/v1/messaging/messages/email') throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); @@ -2989,7 +2989,7 @@ App::post('/v1/messaging/messages/email') 'region' => $project->getAttribute('region'), 'resourceType' => 'message', 'resourceId' => $message->getId(), - 'resourceInternalId' => $message->getInternalId(), + 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), 'projectId' => $project->getId(), 'schedule' => $scheduledAt, @@ -3112,7 +3112,7 @@ App::post('/v1/messaging/messages/sms') 'region' => $project->getAttribute('region'), 'resourceType' => 'message', 'resourceId' => $message->getId(), - 'resourceInternalId' => $message->getInternalId(), + 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), 'projectId' => $project->getId(), 'schedule' => $scheduledAt, @@ -3232,7 +3232,7 @@ App::post('/v1/messaging/messages/push') throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } @@ -3330,7 +3330,7 @@ App::post('/v1/messaging/messages/push') 'region' => $project->getAttribute('region'), 'resourceType' => 'message', 'resourceId' => $message->getId(), - 'resourceInternalId' => $message->getInternalId(), + 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), 'projectId' => $project->getId(), 'schedule' => $scheduledAt, @@ -3731,7 +3731,7 @@ App::patch('/v1/messaging/messages/email/:messageId') 'region' => $project->getAttribute('region'), 'resourceType' => 'message', 'resourceId' => $message->getId(), - 'resourceInternalId' => $message->getInternalId(), + 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), 'projectId' => $project->getId(), 'schedule' => $scheduledAt, @@ -3796,7 +3796,7 @@ App::patch('/v1/messaging/messages/email/:messageId') throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); @@ -3933,7 +3933,7 @@ App::patch('/v1/messaging/messages/sms/:messageId') 'region' => $project->getAttribute('region'), 'resourceType' => 'message', 'resourceId' => $message->getId(), - 'resourceInternalId' => $message->getInternalId(), + 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), 'projectId' => $project->getId(), 'schedule' => $scheduledAt, @@ -4107,7 +4107,7 @@ App::patch('/v1/messaging/messages/push/:messageId') 'region' => $project->getAttribute('region'), 'resourceType' => 'message', 'resourceId' => $message->getId(), - 'resourceInternalId' => $message->getInternalId(), + 'resourceInternalId' => $message->getSequence(), 'resourceUpdatedAt' => DateTime::now(), 'projectId' => $project->getId(), 'schedule' => $scheduledAt, @@ -4210,7 +4210,7 @@ App::patch('/v1/messaging/messages/push/:messageId') throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index 494ccfcaac..92fca88744 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -345,7 +345,7 @@ App::post('/v1/migrations/csv') throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index 047179b888..d09470ff39 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -150,7 +150,7 @@ App::get('/v1/project/usage') $executionsBreakdown = array_map(function ($function) use ($dbForProject) { $id = $function->getId(); $name = $function->getAttribute('name'); - $metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS); + $metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS); $value = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) @@ -166,7 +166,7 @@ App::get('/v1/project/usage') $executionsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) { $id = $function->getId(); $name = $function->getAttribute('name'); - $metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS); + $metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS); $value = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) @@ -182,7 +182,7 @@ App::get('/v1/project/usage') $buildsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) { $id = $function->getId(); $name = $function->getAttribute('name'); - $metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS); + $metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS); $value = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) @@ -198,7 +198,7 @@ App::get('/v1/project/usage') $bucketsBreakdown = array_map(function ($bucket) use ($dbForProject) { $id = $bucket->getId(); $name = $bucket->getAttribute('name'); - $metric = str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE); + $metric = str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_STORAGE); $value = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) @@ -214,7 +214,7 @@ App::get('/v1/project/usage') $databasesStorageBreakdown = array_map(function ($database) use ($dbForProject) { $id = $database->getId(); $name = $database->getAttribute('name'); - $metric = str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE); + $metric = str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_STORAGE); $value = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -231,13 +231,13 @@ App::get('/v1/project/usage') $functionsStorageBreakdown = array_map(function ($function) use ($dbForProject) { $id = $function->getId(); $name = $function->getAttribute('name'); - $deploymentMetric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE); + $deploymentMetric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE); $deploymentValue = $dbForProject->findOne('stats', [ Query::equal('metric', [$deploymentMetric]), Query::equal('period', ['inf']) ]); - $buildMetric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE); + $buildMetric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE); $buildValue = $dbForProject->findOne('stats', [ Query::equal('metric', [$buildMetric]), Query::equal('period', ['inf']) @@ -255,7 +255,7 @@ App::get('/v1/project/usage') $executionsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) { $id = $function->getId(); $name = $function->getAttribute('name'); - $metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS); + $metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS); $value = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) @@ -271,7 +271,7 @@ App::get('/v1/project/usage') $buildsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) { $id = $function->getId(); $name = $function->getAttribute('name'); - $metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS); + $metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS); $value = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index e158cf52fb..51cbc097f5 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -24,6 +24,7 @@ use Utopia\App; use Utopia\Audit\Audit; use Utopia\Cache\Cache; use Utopia\Config\Config; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -181,7 +182,7 @@ App::post('/v1/projects') Permission::delete(Role::team(ID::custom($teamId), 'developer')), ], 'name' => $name, - 'teamInternalId' => $team->getInternalId(), + 'teamInternalId' => $team->getSequence(), 'teamId' => $team->getId(), 'region' => $region, 'description' => $description, @@ -223,19 +224,19 @@ App::post('/v1/projects') $sharedTables = $sharedTablesV1 || $sharedTablesV2; if (!$sharedTablesV2) { - $adapter = $pools->get($dsn->getHost())->pop()->getResource(); + $adapter = new DatabasePool($pools->get($dsn->getHost())); $dbForProject = new Database($adapter, $cache); if ($sharedTables) { $dbForProject ->setSharedTables(true) - ->setTenant($sharedTablesV1 ? $project->getInternalId() : null) + ->setTenant($sharedTablesV1 ? $project->getSequence() : null) ->setNamespace($dsn->getParam('namespace')); } else { $dbForProject ->setSharedTables(false) ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); + ->setNamespace('_' . $project->getSequence()); } $create = true; @@ -503,12 +504,12 @@ App::patch('/v1/projects/:projectId/team') $project ->setAttribute('teamId', $teamId) - ->setAttribute('teamInternalId', $team->getInternalId()) + ->setAttribute('teamInternalId', $team->getSequence()) ->setAttribute('$permissions', $permissions); $project = $dbForPlatform->updateDocument('projects', $project->getId(), $project); $installations = $dbForPlatform->find('installations', [ - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), ]); foreach ($installations as $installation) { $installation->getAttribute('$permissions', $permissions); @@ -516,7 +517,7 @@ App::patch('/v1/projects/:projectId/team') } $repositories = $dbForPlatform->find('repositories', [ - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), ]); foreach ($repositories as $repository) { $repository->getAttribute('$permissions', $permissions); @@ -524,7 +525,7 @@ App::patch('/v1/projects/:projectId/team') } $vcsComments = $dbForPlatform->find('vcsComments', [ - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), ]); foreach ($vcsComments as $vcsComment) { $vcsComment->getAttribute('$permissions', $permissions); @@ -1228,7 +1229,7 @@ App::post('/v1/projects/:projectId/webhooks') Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'projectId' => $project->getId(), 'name' => $name, 'events' => $events, @@ -1278,7 +1279,7 @@ App::get('/v1/projects/:projectId/webhooks') } $webhooks = $dbForPlatform->find('webhooks', [ - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), Query::limit(5000), ]); @@ -1319,7 +1320,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId') $webhook = $dbForPlatform->findOne('webhooks', [ Query::equal('$id', [$webhookId]), - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), ]); if ($webhook->isEmpty()) { @@ -1369,7 +1370,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') $webhook = $dbForPlatform->findOne('webhooks', [ Query::equal('$id', [$webhookId]), - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), ]); if ($webhook->isEmpty()) { @@ -1426,7 +1427,7 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') $webhook = $dbForPlatform->findOne('webhooks', [ Query::equal('$id', [$webhookId]), - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), ]); if ($webhook->isEmpty()) { @@ -1473,7 +1474,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') $webhook = $dbForPlatform->findOne('webhooks', [ Query::equal('$id', [$webhookId]), - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), ]); if ($webhook->isEmpty()) { @@ -1527,7 +1528,7 @@ App::post('/v1/projects/:projectId/keys') Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'projectId' => $project->getId(), 'name' => $name, 'scopes' => $scopes, @@ -1575,7 +1576,7 @@ App::get('/v1/projects/:projectId/keys') } $keys = $dbForPlatform->find('keys', [ - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), Query::limit(5000), ]); @@ -1616,7 +1617,7 @@ App::get('/v1/projects/:projectId/keys/:keyId') $key = $dbForPlatform->findOne('keys', [ Query::equal('$id', [$keyId]), - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), ]); if ($key->isEmpty()) { @@ -1660,7 +1661,7 @@ App::put('/v1/projects/:projectId/keys/:keyId') $key = $dbForPlatform->findOne('keys', [ Query::equal('$id', [$keyId]), - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), ]); if ($key->isEmpty()) { @@ -1711,7 +1712,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId') $key = $dbForPlatform->findOne('keys', [ Query::equal('$id', [$keyId]), - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), ]); if ($key->isEmpty()) { @@ -1810,7 +1811,7 @@ App::post('/v1/projects/:projectId/platforms') Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'projectId' => $project->getId(), 'type' => $type, 'name' => $name, @@ -1857,7 +1858,7 @@ App::get('/v1/projects/:projectId/platforms') } $platforms = $dbForPlatform->find('platforms', [ - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), Query::limit(5000), ]); @@ -1898,7 +1899,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId') $platform = $dbForPlatform->findOne('platforms', [ Query::equal('$id', [$platformId]), - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), ]); if ($platform->isEmpty()) { @@ -1942,7 +1943,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') $platform = $dbForPlatform->findOne('platforms', [ Query::equal('$id', [$platformId]), - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), ]); if ($platform->isEmpty()) { @@ -1996,7 +1997,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') $platform = $dbForPlatform->findOne('platforms', [ Query::equal('$id', [$platformId]), - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), ]); if ($platform->isEmpty()) { diff --git a/app/controllers/api/proxy.php b/app/controllers/api/proxy.php index e96f8bfb2c..417ea602ba 100644 --- a/app/controllers/api/proxy.php +++ b/app/controllers/api/proxy.php @@ -58,7 +58,7 @@ App::get('/v1/proxy/rules') $queries[] = Query::search('search', $search); } - $queries[] = Query::equal('projectInternalId', [$project->getInternalId()]); + $queries[] = Query::equal('projectInternalId', [$project->getSequence()]); /** * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries @@ -124,7 +124,7 @@ App::get('/v1/proxy/rules/:ruleId') ->action(function (string $ruleId, Response $response, Document $project, Database $dbForPlatform) { $rule = $dbForPlatform->getDocument('rules', $ruleId); - if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getInternalId()) { + if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) { throw new Exception(Exception::RULE_NOT_FOUND); } @@ -165,7 +165,7 @@ App::delete('/v1/proxy/rules/:ruleId') ->action(function (string $ruleId, Response $response, Document $project, Database $dbForPlatform, Delete $queueForDeletes, Event $queueForEvents) { $rule = $dbForPlatform->getDocument('rules', $ruleId); - if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getInternalId()) { + if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) { throw new Exception(Exception::RULE_NOT_FOUND); } @@ -210,15 +210,46 @@ App::patch('/v1/proxy/rules/:ruleId/verification') ->action(function (string $ruleId, Response $response, Certificate $queueForCertificates, Event $queueForEvents, Document $project, Database $dbForPlatform, Log $log) { $rule = $dbForPlatform->getDocument('rules', $ruleId); - if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getInternalId()) { + if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) { throw new Exception(Exception::RULE_NOT_FOUND); } - $validators = []; - $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')); - if ($targetCNAME->isKnown() && !$targetCNAME->isTest()) { - $validators[] = new DNS($targetCNAME->get(), DNS::RECORD_CNAME); + $targetCNAME = null; + switch ($rule->getAttribute('type', '')) { + case 'api': + // For example: fra.cloud.appwrite.io + $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')); + break; + case 'redirect': + // For example: appwrite.network + $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_SITES', '')); + break; + case 'deployment': + switch ($rule->getAttribute('deploymentResourceType', '')) { + case 'function': + // For example: fra.appwrite.run + $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_FUNCTIONS', '')); + break; + case 'site': + // For example: appwrite.network + $targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_SITES', '')); + break; + default: + break; + } + // no break + default: + break; } + + $validators = []; + + if (!is_null($targetCNAME)) { + if ($targetCNAME->isKnown() && !$targetCNAME->isTest()) { + $validators[] = new DNS($targetCNAME->get(), DNS::RECORD_CNAME); + } + } + if ((new IP(IP::V4))->isValid(System::getEnv('_APP_DOMAIN_TARGET_A', ''))) { $validators[] = new DNS(System::getEnv('_APP_DOMAIN_TARGET_A', ''), DNS::RECORD_A); } @@ -260,7 +291,8 @@ App::patch('/v1/proxy/rules/:ruleId/verification') // Issue a TLS certificate when domain is verified $queueForCertificates ->setDomain(new Document([ - 'domain' => $rule->getAttribute('domain') + 'domain' => $rule->getAttribute('domain'), + 'domainType' => $rule->getAttribute('deploymentResourceType', $rule->getAttribute('type')), ])) ->trigger(); diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index ee6279c3bc..26c789ce27 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -145,7 +145,7 @@ App::post('/v1/storage/buckets') $bucket = $dbForProject->getDocument('buckets', $bucketId); - $dbForProject->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes, permissions: $permissions ?? [], documentSecurity: $fileSecurity); + $dbForProject->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes, permissions: $permissions ?? [], documentSecurity: $fileSecurity); } catch (DuplicateException) { throw new Exception(Exception::STORAGE_BUCKET_ALREADY_EXISTS); } @@ -316,17 +316,17 @@ App::put('/v1/storage/buckets/:bucketId') $permissions = Permission::aggregate($permissions); $bucket = $dbForProject->updateDocument('buckets', $bucket->getId(), $bucket - ->setAttribute('name', $name) - ->setAttribute('$permissions', $permissions) - ->setAttribute('maximumFileSize', $maximumFileSize) - ->setAttribute('allowedFileExtensions', $allowedFileExtensions) - ->setAttribute('fileSecurity', $fileSecurity) - ->setAttribute('enabled', $enabled) - ->setAttribute('encryption', $encryption) - ->setAttribute('compression', $compression) - ->setAttribute('antivirus', $antivirus)); + ->setAttribute('name', $name) + ->setAttribute('$permissions', $permissions) + ->setAttribute('maximumFileSize', $maximumFileSize) + ->setAttribute('allowedFileExtensions', $allowedFileExtensions) + ->setAttribute('fileSecurity', $fileSecurity) + ->setAttribute('enabled', $enabled) + ->setAttribute('encryption', $encryption) + ->setAttribute('compression', $compression) + ->setAttribute('antivirus', $antivirus)); - $dbForProject->updateCollection('bucket_' . $bucket->getInternalId(), $permissions, $fileSecurity); + $dbForProject->updateCollection('bucket_' . $bucket->getSequence(), $permissions, $fileSecurity); $queueForEvents ->setParam('bucketId', $bucket->getId()); @@ -558,7 +558,7 @@ App::post('/v1/storage/buckets/:bucketId/files') $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); + $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); $metadata = ['content_type' => $deviceForLocal->getFileMimeType($fileTmpName)]; if (!$file->isEmpty()) { @@ -652,7 +652,7 @@ App::post('/v1/storage/buckets/:bucketId/files') '$id' => $fileId, '$permissions' => $permissions, 'bucketId' => $bucket->getId(), - 'bucketInternalId' => $bucket->getInternalId(), + 'bucketInternalId' => $bucket->getSequence(), 'name' => $fileName, 'path' => $path, 'signature' => $fileHash, @@ -671,7 +671,7 @@ App::post('/v1/storage/buckets/:bucketId/files') 'metadata' => $metadata, ]); - $file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc); + $file = $dbForProject->createDocument('bucket_' . $bucket->getSequence(), $doc); } else { $file = $file ->setAttribute('$permissions', $permissions) @@ -696,7 +696,7 @@ App::post('/v1/storage/buckets/:bucketId/files') if (!$validator->isValid($bucket->getCreate())) { throw new Exception(Exception::USER_UNAUTHORIZED); } - $file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); + $file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file)); } } else { if ($file->isEmpty()) { @@ -704,7 +704,7 @@ App::post('/v1/storage/buckets/:bucketId/files') '$id' => ID::custom($fileId), '$permissions' => $permissions, 'bucketId' => $bucket->getId(), - 'bucketInternalId' => $bucket->getInternalId(), + 'bucketInternalId' => $bucket->getSequence(), 'name' => $fileName, 'path' => $path, 'signature' => '', @@ -720,7 +720,7 @@ App::post('/v1/storage/buckets/:bucketId/files') ]); try { - $file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc); + $file = $dbForProject->createDocument('bucket_' . $bucket->getSequence(), $doc); } catch (NotFoundException) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } @@ -741,7 +741,7 @@ App::post('/v1/storage/buckets/:bucketId/files') } try { - $file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); + $file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file)); } catch (NotFoundException) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } @@ -826,9 +826,9 @@ App::get('/v1/storage/buckets/:bucketId/files') $fileId = $cursor->getValue(); if ($fileSecurity && !$valid) { - $cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + $cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); } else { - $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); } if ($cursorDocument->isEmpty()) { @@ -842,11 +842,11 @@ App::get('/v1/storage/buckets/:bucketId/files') try { if ($fileSecurity && !$valid) { - $files = $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries); - $total = $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT); + $files = $dbForProject->find('bucket_' . $bucket->getSequence(), $queries); + $total = $dbForProject->count('bucket_' . $bucket->getSequence(), $filterQueries, APP_LIMIT_COUNT); } else { - $files = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries)); - $total = Authorization::skip(fn () => $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT)); + $files = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getSequence(), $queries)); + $total = Authorization::skip(fn () => $dbForProject->count('bucket_' . $bucket->getSequence(), $filterQueries, APP_LIMIT_COUNT)); } } catch (NotFoundException) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -902,9 +902,9 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') } if ($fileSecurity && !$valid) { - $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); } else { - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); } if ($file->isEmpty()) { @@ -973,7 +973,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - $isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getInternalId(); + $isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence(); $fileSecurity = $bucket->getAttribute('fileSecurity', false); $validator = new Authorization(Database::PERMISSION_READ); $valid = $validator->isValid($bucket->getRead()); @@ -982,12 +982,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') } if ($fileSecurity && !$valid && !$isToken) { - $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); } else { - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); } - if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getInternalId()) { + if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -1004,7 +1004,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $algorithm = $file->getAttribute('algorithm', Compression::NONE); $cipher = $file->getAttribute('openSSLCipher'); $mime = $file->getAttribute('mimeType'); - if (!\in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) System::getEnv('_APP_STORAGE_PREVIEW_LIMIT', 20000000)) { + if (!\in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) System::getEnv('_APP_STORAGE_PREVIEW_LIMIT', APP_STORAGE_READ_BUFFER)) { if (!\in_array($mime, $inputs)) { $path = (\array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default']; } else { @@ -1150,7 +1150,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - $isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getInternalId(); + $isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence(); $fileSecurity = $bucket->getAttribute('fileSecurity', false); $validator = new Authorization(Database::PERMISSION_READ); $valid = $validator->isValid($bucket->getRead()); @@ -1159,12 +1159,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') } if ($fileSecurity && !$valid) { - $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); } else { - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); } - if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getInternalId()) { + if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -1178,13 +1178,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path); } - $response - ->setContentType($file->getAttribute('mimeType')) - ->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days - ->addHeader('X-Peak', \memory_get_peak_usage()) - ->addHeader('Content-Disposition', 'attachment; filename="' . $file->getAttribute('name', '') . '"') - ; - $size = $file->getAttribute('sizeOriginal', 0); $rangeHeader = $request->getHeader('range'); @@ -1193,7 +1186,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') $end = $request->getRangeEnd(); $unit = $request->getRangeUnit(); - if ($end === null) { + if ($end === null || $end - $start > APP_STORAGE_READ_BUFFER) { $end = min(($start + MAX_OUTPUT_CHUNK_SIZE - 1), ($size - 1)); } @@ -1208,6 +1201,13 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') ->setStatusCode(Response::STATUS_CODE_PARTIALCONTENT); } + $response + ->setContentType($file->getAttribute('mimeType')) + ->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days + ->addHeader('X-Peak', \memory_get_peak_usage()) + ->addHeader('Content-Disposition', 'attachment; filename="' . $file->getAttribute('name', '') . '"') + ; + $source = ''; if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt $source = $deviceForFiles->read($path); @@ -1241,12 +1241,15 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') if (!empty($source)) { if (!empty($rangeHeader)) { $response->send(substr($source, $start, ($end - $start + 1))); + return; } $response->send($source); + return; } if (!empty($rangeHeader)) { $response->send($deviceForFiles->read($path, $start, ($end - $start + 1))); + return; } if ($size > APP_STORAGE_READ_BUFFER) { @@ -1306,7 +1309,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - $isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getInternalId(); + $isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence(); $fileSecurity = $bucket->getAttribute('fileSecurity', false); $validator = new Authorization(Database::PERMISSION_READ); $valid = $validator->isValid($bucket->getRead()); @@ -1315,12 +1318,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') } if ($fileSecurity && !$valid) { - $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); } else { - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); } - if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getInternalId()) { + if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -1342,15 +1345,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') $contentType = $file->getAttribute('mimeType'); } - $response - ->setContentType($contentType) - ->addHeader('Content-Security-Policy', 'script-src none;') - ->addHeader('X-Content-Type-Options', 'nosniff') - ->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"') - ->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days - ->addHeader('X-Peak', \memory_get_peak_usage()) - ; - $size = $file->getAttribute('sizeOriginal', 0); $rangeHeader = $request->getHeader('range'); @@ -1359,8 +1353,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') $end = $request->getRangeEnd(); $unit = $request->getRangeUnit(); - if ($end === null) { - $end = min(($start + 2000000 - 1), ($size - 1)); + if ($end === null || $end - $start > APP_STORAGE_READ_BUFFER) { + $end = min(($start + APP_STORAGE_READ_BUFFER - 1), ($size - 1)); } if ($unit != 'bytes' || $start >= $end || $end >= $size) { @@ -1374,6 +1368,15 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') ->setStatusCode(Response::STATUS_CODE_PARTIALCONTENT); } + $response + ->setContentType($contentType) + ->addHeader('Content-Security-Policy', 'script-src none;') + ->addHeader('X-Content-Type-Options', 'nosniff') + ->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"') + ->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days + ->addHeader('X-Peak', \memory_get_peak_usage()) + ; + $source = ''; if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt $source = $deviceForFiles->read($path); @@ -1407,6 +1410,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') if (!empty($source)) { if (!empty($rangeHeader)) { $response->send(substr($source, $start, ($end - $start + 1))); + return; } $response->send($source); return; @@ -1474,7 +1478,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push') throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); @@ -1494,14 +1498,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push') $contentType = $file->getAttribute('mimeType'); } - $response - ->setContentType($contentType) - ->addHeader('Content-Security-Policy', 'script-src none;') - ->addHeader('X-Content-Type-Options', 'nosniff') - ->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"') - ->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days - ->addHeader('X-Peak', \memory_get_peak_usage()); - $size = $file->getAttribute('sizeOriginal', 0); $rangeHeader = $request->getHeader('range'); @@ -1510,8 +1506,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push') $end = $request->getRangeEnd(); $unit = $request->getRangeUnit(); - if ($end === null) { - $end = min(($start + 2000000 - 1), ($size - 1)); + if ($end === null || $end - $start > APP_STORAGE_READ_BUFFER) { + $end = min(($start + APP_STORAGE_READ_BUFFER - 1), ($size - 1)); } if ($unit != 'bytes' || $start >= $end || $end >= $size) { @@ -1525,6 +1521,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push') ->setStatusCode(Response::STATUS_CODE_PARTIALCONTENT); } + $response + ->setContentType($contentType) + ->addHeader('Content-Security-Policy', 'script-src none;') + ->addHeader('X-Content-Type-Options', 'nosniff') + ->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"') + ->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days + ->addHeader('X-Peak', \memory_get_peak_usage()); + $source = ''; if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt $source = $deviceForFiles->read($path); @@ -1558,6 +1562,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push') if (!empty($source)) { if (!empty($rangeHeader)) { $response->send(substr($source, $start, ($end - $start + 1))); + return; } $response->send($source); return; @@ -1638,7 +1643,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') } // Read permission should not be required for update - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); @@ -1684,9 +1689,9 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') try { if ($fileSecurity && !$valid) { - $file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file); + $file = $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file); } else { - $file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); + $file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file)); } } catch (NotFoundException) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -1752,7 +1757,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') } // Read permission should not be required for delete - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); @@ -1782,9 +1787,9 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') try { if ($fileSecurity && !$valid) { - $deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId); + $deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getSequence(), $fileId); } else { - $deleted = Authorization::skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $deleted = Authorization::skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getSequence(), $fileId)); } } catch (NotFoundException) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -1932,14 +1937,14 @@ App::get('/v1/storage/:bucketId/usage') $stats = $usage = []; $days = $periods[$range]; $metrics = [ - str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES), - str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE), - str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED), + str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES), + str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_STORAGE), + str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED), ]; Authorization::skip(function () use ($dbForProject, $dbForLogs, $bucket, $days, $metrics, &$stats) { foreach ($metrics as $metric) { - $db = ($metric === str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED)) + $db = ($metric === str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED)) ? $dbForLogs : $dbForProject; diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 49d9005c54..e72ea7bc92 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -123,9 +123,9 @@ App::post('/v1/teams') Permission::delete(Role::team($team->getId(), 'owner')), ], 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'teamId' => $team->getId(), - 'teamInternalId' => $team->getInternalId(), + 'teamInternalId' => $team->getSequence(), 'roles' => $roles, 'invited' => DateTime::now(), 'joined' => DateTime::now(), @@ -595,8 +595,8 @@ App::post('/v1/teams/:teamId/memberships') } $membership = $dbForProject->findOne('memberships', [ - Query::equal('userInternalId', [$invitee->getInternalId()]), - Query::equal('teamInternalId', [$team->getInternalId()]), + Query::equal('userInternalId', [$invitee->getSequence()]), + Query::equal('teamInternalId', [$team->getSequence()]), ]); $secret = Auth::tokenGenerator(); @@ -612,9 +612,9 @@ App::post('/v1/teams/:teamId/memberships') Permission::delete(Role::team($team->getId(), 'owner')), ], 'userId' => $invitee->getId(), - 'userInternalId' => $invitee->getInternalId(), + 'userInternalId' => $invitee->getSequence(), 'teamId' => $team->getId(), - 'teamInternalId' => $team->getInternalId(), + 'teamInternalId' => $team->getSequence(), 'roles' => $roles, 'invited' => DateTime::now(), 'joined' => ($isPrivilegedUser || $isAppUser) ? DateTime::now() : null, @@ -842,7 +842,7 @@ App::get('/v1/teams/:teamId/memberships') } // Set internal queries - $queries[] = Query::equal('teamInternalId', [$team->getInternalId()]); + $queries[] = Query::equal('teamInternalId', [$team->getSequence()]); /** * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries @@ -1092,15 +1092,18 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') collection: 'memberships', queries: [ Query::contains('roles', ['owner']), - Query::equal('teamInternalId', [$team->getInternalId()]) + Query::equal('teamInternalId', [$team->getSequence()]) ], max: 2 ); + // Is the role change being requested by the user on their own membership? + $isCurrentUserAnOwner = $user->getSequence() === $membership->getAttribute('userInternalId'); + // Prevent role change if there's only one owner left, - // the requester is that owner, and the new `$roles` no longer include 'owner'! - if ($ownersCount === 1 && $isOwner && !\in_array('owner', $roles)) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'There must be at least one owner in the organization.'); + // the requester is that owner, and the new `$roles` no longer include 'owner' + if ($ownersCount === 1 && $isOwner && $isCurrentUserAnOwner && !\in_array('owner', $roles)) { + throw new Exception(Exception::MEMBERSHIP_DOWNGRADE_PROHIBITED, 'There must be at least one owner in the organization.'); } } @@ -1180,7 +1183,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') throw new Exception(Exception::TEAM_NOT_FOUND); } - if ($membership->getAttribute('teamInternalId') !== $team->getInternalId()) { + if ($membership->getAttribute('teamInternalId') !== $team->getSequence()) { throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH); } @@ -1197,7 +1200,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') $user->setAttributes($dbForProject->getDocument('users', $userId)->getArrayCopy()); // Get user } - if ($membership->getAttribute('userInternalId') !== $user->getInternalId()) { + if ($membership->getAttribute('userInternalId') !== $user->getSequence()) { throw new Exception(Exception::TEAM_INVITE_MISMATCH, 'Invite does not belong to current user (' . $user->getAttribute('email') . ')'); } @@ -1229,7 +1232,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') Permission::delete(Role::user($user->getId())), ], 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'provider' => Auth::SESSION_PROVIDER_EMAIL, 'providerUid' => $user->getAttribute('email'), 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak @@ -1315,10 +1318,12 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') )) ->param('teamId', '', new UID(), 'Team ID.') ->param('membershipId', '', new UID(), 'Membership ID.') + ->inject('user') + ->inject('project') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $teamId, string $membershipId, Document $user, Document $project, Response $response, Database $dbForProject, Event $queueForEvents) { $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -1326,9 +1331,9 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') throw new Exception(Exception::TEAM_INVITE_NOT_FOUND); } - $user = $dbForProject->getDocument('users', $membership->getAttribute('userId')); + $profile = $dbForProject->getDocument('users', $membership->getAttribute('userId')); - if ($user->isEmpty()) { + if ($profile->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); } @@ -1338,10 +1343,33 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') throw new Exception(Exception::TEAM_NOT_FOUND); } - if ($membership->getAttribute('teamInternalId') !== $team->getInternalId()) { + if ($membership->getAttribute('teamInternalId') !== $team->getSequence()) { throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH); } + if ($project->getId() === 'console') { + // Quick check: + // fetch up to 2 owners to determine if only one exists + $ownersCount = $dbForProject->count( + collection: 'memberships', + queries: [ + Query::contains('roles', ['owner']), + Query::equal('teamInternalId', [$team->getSequence()]) + ], + max: 2 + ); + + // Is the deletion being requested by the user on their own membership and they are also the owner? + $isSelfOwner = + in_array('owner', $membership->getAttribute('roles')) && + $membership->getAttribute('userInternalId') === $user->getSequence(); + + if ($ownersCount === 1 && $isSelfOwner) { + /* Prevent removal if the user is the only owner. */ + throw new Exception(Exception::MEMBERSHIP_DELETION_PROHIBITED, 'There must be at least one owner in the organization.'); + } + } + try { $dbForProject->deleteDocument('memberships', $membership->getId()); } catch (AuthorizationException $exception) { @@ -1350,15 +1378,15 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove membership from DB'); } - $dbForProject->purgeCachedDocument('users', $user->getId()); + $dbForProject->purgeCachedDocument('users', $profile->getId()); if ($membership->getAttribute('confirm')) { // Count only confirmed members Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0)); } $queueForEvents - ->setParam('userId', $user->getId()) ->setParam('teamId', $team->getId()) + ->setParam('userId', $profile->getId()) ->setParam('membershipId', $membership->getId()) ->setPayload($response->output($membership, Response::MODEL_MEMBERSHIP)) ; diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index bc9de0fd42..d7def69464 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -140,7 +140,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e Permission::delete(Role::user($user->getId())), ], 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'providerType' => 'email', 'identifier' => $email, ])); @@ -164,7 +164,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e Permission::delete(Role::user($user->getId())), ], 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'providerType' => 'sms', 'identifier' => $phone, ])); @@ -564,10 +564,10 @@ App::post('/v1/users/:userId/targets') Permission::delete(Role::user($user->getId())), ], 'providerId' => empty($provider->getId()) ? null : $provider->getId(), - 'providerInternalId' => $provider->isEmpty() ? null : $provider->getInternalId(), + 'providerInternalId' => $provider->isEmpty() ? null : $provider->getSequence(), 'providerType' => $providerType, 'userId' => $userId, - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'identifier' => $identifier, 'name' => ($name !== '') ? $name : null, ])); @@ -846,7 +846,7 @@ App::get('/v1/users/:userId/memberships') } // Set internal queries - $queries[] = Query::equal('userInternalId', [$user->getInternalId()]); + $queries[] = Query::equal('userInternalId', [$user->getSequence()]); $memberships = array_map(function ($membership) use ($dbForProject, $user) { $team = $dbForProject->getDocument('teams', $membership->getAttribute('teamId')); @@ -910,7 +910,7 @@ App::get('/v1/users/:userId/logs') $audit = new Audit($dbForProject); - $logs = $audit->getLogsByUser($user->getInternalId(), $queries); + $logs = $audit->getLogsByUser($user->getSequence(), $queries); $output = []; @@ -957,7 +957,7 @@ App::get('/v1/users/:userId/logs') } $response->dynamic(new Document([ - 'total' => $audit->countLogsByUser($user->getInternalId(), $queries), + 'total' => $audit->countLogsByUser($user->getSequence(), $queries), 'logs' => $output, ]), Response::MODEL_LOG_LIST); }); @@ -980,7 +980,7 @@ App::get('/v1/users/:userId/targets') ] )) ->param('userId', '', new UID(), 'User ID.') - ->param('queries', [], new Targets(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Users::ALLOWED_ATTRIBUTES), true) + ->param('queries', [], new Targets(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Targets::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') ->action(function (string $userId, array $queries, Response $response, Database $dbForProject) { @@ -1396,7 +1396,7 @@ App::patch('/v1/users/:userId/email') // Makes sure this email is not already used in another identity $identityWithMatchingEmail = $dbForProject->findOne('identities', [ Query::equal('providerEmail', [$email]), - Query::notEqual('userInternalId', $user->getInternalId()), + Query::notEqual('userInternalId', $user->getSequence()), ]); if (!$identityWithMatchingEmail->isEmpty()) { throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS); @@ -1440,7 +1440,7 @@ App::patch('/v1/users/:userId/email') Permission::delete(Role::user($user->getId())), ], 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'providerType' => 'email', 'identifier' => $email, ])); @@ -1529,7 +1529,7 @@ App::patch('/v1/users/:userId/phone') Permission::delete(Role::user($user->getId())), ], 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'providerType' => 'sms', 'identifier' => $number, ])); @@ -1711,7 +1711,7 @@ App::patch('/v1/users/:userId/targets/:targetId') $target ->setAttribute('providerId', $provider->getId()) - ->setAttribute('providerInternalId', $provider->getInternalId()); + ->setAttribute('providerInternalId', $provider->getSequence()); } if ($name) { @@ -2051,7 +2051,7 @@ App::post('/v1/users/:userId/sessions') [ '$id' => ID::unique(), 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'provider' => Auth::SESSION_PROVIDER_SERVER, 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'userAgent' => $request->getUserAgent('UNKNOWN'), @@ -2131,7 +2131,7 @@ App::post('/v1/users/:userId/tokens') $token = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userInternalId' => $user->getSequence(), 'type' => Auth::TOKEN_TYPE_GENERIC, 'secret' => Auth::hash($secret), 'expire' => $expire, @@ -2293,8 +2293,8 @@ App::delete('/v1/users/:userId') $clone = clone $user; $dbForProject->deleteDocument('users', $userId); - DeleteIdentities::delete($dbForProject, Query::equal('userInternalId', [$user->getInternalId()])); - DeleteTargets::delete($dbForProject, Query::equal('userInternalId', [$user->getInternalId()])); + DeleteIdentities::delete($dbForProject, Query::equal('userInternalId', [$user->getSequence()])); + DeleteTargets::delete($dbForProject, Query::equal('userInternalId', [$user->getSequence()])); $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 571c7ddca7..31b2539b99 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -78,11 +78,11 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $resourceCollection = $resourceType === "function" ? 'functions' : 'sites'; $resourceId = $repository->getAttribute('resourceId'); $resource = Authorization::skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId)); - $resourceInternalId = $resource->getInternalId(); + $resourceInternalId = $resource->getSequence(); $deploymentId = ID::unique(); $repositoryId = $repository->getId(); - $repositoryInternalId = $repository->getInternalId(); + $repositoryInternalId = $repository->getSequence(); $providerRepositoryId = $repository->getAttribute('providerRepositoryId'); $installationId = $repository->getAttribute('installationId'); $installationInternalId = $repository->getAttribute('installationInternalId'); @@ -157,7 +157,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId ], 'installationInternalId' => $installationInternalId, 'installationId' => $installationId, - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'projectId' => $project->getId(), 'providerRepositoryId' => $providerRepositoryId, 'providerBranch' => $providerBranch, @@ -257,7 +257,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $resource = $resource ->setAttribute('latestDeploymentId', $deployment->getId()) - ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getSequence()) ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); Authorization::skip(fn () => $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource)); @@ -273,12 +273,12 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'domain' => $domain, 'type' => 'deployment', 'trigger' => 'deployment', 'deploymentId' => $deployment->getId(), - 'deploymentInternalId' => $deployment->getInternalId(), + 'deploymentInternalId' => $deployment->getSequence(), 'deploymentResourceType' => 'site', 'deploymentResourceId' => $resourceId, 'deploymentResourceInternalId' => $resourceInternalId, @@ -293,19 +293,25 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId // VCS branch preview if (!empty($providerBranch)) { - $domain = "branch-{$providerBranch}-{$resource->getId()}-{$project->getId()}.{$sitesDomain}"; + $branchPrefix = substr($providerBranch, 0, 16); + if (strlen($providerBranch) > 16) { + $remainingChars = substr($providerBranch, 16); + $branchPrefix .= '-' . substr(hash('sha256', $remainingChars), 0, 7); + } + $resourceProjectHash = substr(hash('sha256', $resource->getId() . $project->getId()), 0, 7); + $domain = "branch-{$branchPrefix}-{$resourceProjectHash}.{$sitesDomain}"; $ruleId = md5($domain); try { Authorization::skip( fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'domain' => $domain, 'type' => 'deployment', 'trigger' => 'deployment', 'deploymentId' => $deployment->getId(), - 'deploymentInternalId' => $deployment->getInternalId(), + 'deploymentInternalId' => $deployment->getSequence(), 'deploymentResourceType' => 'site', 'deploymentResourceId' => $resourceId, 'deploymentResourceInternalId' => $resourceInternalId, @@ -324,19 +330,19 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId // VCS commit preview if (!empty($providerCommitHash)) { - $domain = "commit-{$providerCommitHash}-{$resource->getId()}-{$project->getId()}.{$sitesDomain}"; + $domain = "commit-" . substr($providerCommitHash, 0, 16) . ".{$sitesDomain}"; $ruleId = md5($domain); try { Authorization::skip( fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'domain' => $domain, 'type' => 'deployment', 'trigger' => 'deployment', 'deploymentId' => $deployment->getId(), - 'deploymentInternalId' => $deployment->getInternalId(), + 'deploymentInternalId' => $deployment->getSequence(), 'deploymentResourceType' => 'site', 'deploymentResourceId' => $resourceId, 'deploymentResourceInternalId' => $resourceInternalId, @@ -503,7 +509,7 @@ App::get('/v1/vcs/github/callback') $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); $owner = $github->getOwnerName($providerInstallationId) ?? ''; - $projectInternalId = $project->getInternalId(); + $projectInternalId = $project->getSequence(); $installation = $dbForPlatform->findOne('installations', [ Query::equal('providerInstallationId', [$providerInstallationId]), @@ -999,7 +1005,7 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories') if (empty($accessToken) || empty($refreshToken) || empty($accessTokenExpiry)) { $identity = $dbForPlatform->findOne('identities', [ Query::equal('provider', ['github']), - Query::equal('userInternalId', [$user->getInternalId()]), + Query::equal('userInternalId', [$user->getSequence()]), ]); if ($identity->isEmpty()) { throw new Exception(Exception::USER_IDENTITY_NOT_FOUND); @@ -1209,6 +1215,7 @@ App::post('/v1/vcs/github/events') if ($event == $github::EVENT_PUSH) { $providerBranchCreated = $parsedPayload["branchCreated"] ?? false; + $providerBranchDeleted = $parsedPayload["branchDeleted"] ?? false; $providerBranch = $parsedPayload["branch"] ?? ''; $providerBranchUrl = $parsedPayload["branchUrl"] ?? ''; $providerRepositoryId = $parsedPayload["repositoryId"] ?? ''; @@ -1230,8 +1237,8 @@ App::post('/v1/vcs/github/events') Query::limit(100), ])); - // create new deployment only on push and not when branch is created - if (!$providerBranchCreated) { + // create new deployment only on push and not when branch is created or deleted + if (!$providerBranchCreated && !$providerBranchDeleted) { $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForPlatform, $queueForBuilds, $getProjectDB, $request); } } elseif ($event == $github::EVENT_INSTALLATION) { @@ -1246,7 +1253,7 @@ App::post('/v1/vcs/github/events') foreach ($installations as $installation) { $repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [ - Query::equal('installationInternalId', [$installation->getInternalId()]), + Query::equal('installationInternalId', [$installation->getSequence()]), Query::limit(1000) ])); @@ -1349,7 +1356,7 @@ App::get('/v1/vcs/installations') throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - $queries[] = Query::equal('projectInternalId', [$project->getInternalId()]); + $queries[] = Query::equal('projectInternalId', [$project->getSequence()]); if (!empty($search)) { $queries[] = Query::search('search', $search); @@ -1422,7 +1429,7 @@ App::get('/v1/vcs/installations/:installationId') throw new Exception(Exception::INSTALLATION_NOT_FOUND); } - if ($installation->getAttribute('projectInternalId') !== $project->getInternalId()) { + if ($installation->getAttribute('projectInternalId') !== $project->getSequence()) { throw new Exception(Exception::INSTALLATION_NOT_FOUND); } @@ -1505,7 +1512,7 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor } $repository = Authorization::skip(fn () => $dbForPlatform->getDocument('repositories', $repositoryId, [ - Query::equal('projectInternalId', [$project->getInternalId()]) + Query::equal('projectInternalId', [$project->getSequence()]) ])); if ($repository->isEmpty()) { diff --git a/app/controllers/general.php b/app/controllers/general.php index 1e9e64d3b8..e743f53454 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -391,9 +391,9 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw $execution = new Document([ '$id' => $executionId, '$permissions' => [], - 'resourceInternalId' => $resource->getInternalId(), + 'resourceInternalId' => $resource->getSequence(), 'resourceId' => $resource->getId(), - 'deploymentInternalId' => $deployment->getInternalId(), + 'deploymentInternalId' => $deployment->getSequence(), 'deploymentId' => $deployment->getId(), 'responseStatusCode' => 0, 'responseHeaders' => [], @@ -692,11 +692,11 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw } $metricTypeExecutions = str_replace(['{resourceType}'], [$deployment->getAttribute('resourceType')], METRIC_RESOURCE_TYPE_EXECUTIONS); - $metricTypeIdExecutions = str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS); + $metricTypeIdExecutions = str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS); $metricTypeExecutionsCompute = str_replace(['{resourceType}'], [$deployment->getAttribute('resourceType')], METRIC_RESOURCE_TYPE_EXECUTIONS_COMPUTE); - $metricTypeIdExecutionsCompute = str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE); + $metricTypeIdExecutionsCompute = str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE); $metricTypeExecutionsMbSeconds = str_replace(['{resourceType}'], [$deployment->getAttribute('resourceType')], METRIC_RESOURCE_TYPE_EXECUTIONS_MB_SECONDS); - $metricTypeIdExecutionsMBSeconds = str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS); + $metricTypeIdExecutionsMBSeconds = str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS); if ($deployment->getAttribute('resourceType') === 'sites') { $queueForStatsUsage ->disableMetric(METRIC_NETWORK_REQUESTS) @@ -719,9 +719,9 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw ->addMetric(METRIC_SITES_REQUESTS, 1) ->addMetric(METRIC_SITES_INBOUND, $request->getSize() + $fileSize) ->addMetric(METRIC_SITES_OUTBOUND, $response->getSize()) - ->addMetric(str_replace('{siteInternalId}', $resource->getInternalId(), METRIC_SITES_ID_REQUESTS), 1) - ->addMetric(str_replace('{siteInternalId}', $resource->getInternalId(), METRIC_SITES_ID_INBOUND), $request->getSize() + $fileSize) - ->addMetric(str_replace('{siteInternalId}', $resource->getInternalId(), METRIC_SITES_ID_OUTBOUND), $response->getSize()) + ->addMetric(str_replace('{siteInternalId}', $resource->getSequence(), METRIC_SITES_ID_REQUESTS), 1) + ->addMetric(str_replace('{siteInternalId}', $resource->getSequence(), METRIC_SITES_ID_INBOUND), $request->getSize() + $fileSize) + ->addMetric(str_replace('{siteInternalId}', $resource->getSequence(), METRIC_SITES_ID_OUTBOUND), $response->getSize()) ; } @@ -749,12 +749,6 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw return false; } elseif ($type === 'redirect') { $url = $rule->getAttribute('redirectUrl', ''); - - $query = ($swooleRequest->server['query_string'] ?? ''); - if (!empty($query)) { - $url .= '?' . $query; - } - $response->redirect($url, \intval($rule->getAttribute('redirectStatusCode', 301))); return true; } else { @@ -914,7 +908,7 @@ App::init() 'type' => 'api', 'status' => 'verifying', 'projectId' => $console->getId(), - 'projectInternalId' => $console->getInternalId(), + 'projectInternalId' => $console->getSequence(), 'search' => implode(' ', [$ruleId, $domain->get()]), 'owner' => $owner, 'region' => $console->getAttribute('region') @@ -964,7 +958,7 @@ App::init() )[0] ?? new Document(); } - if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getInternalId()) { + if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) { $refDomainOrigin = $origin; } } @@ -1047,7 +1041,7 @@ App::init() ->addHeader('Server', 'Appwrite') ->addHeader('X-Content-Type-Options', 'nosniff') ->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE') - ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Forwarded-For, X-Forwarded-User-Agent') + ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Dev-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Forwarded-For, X-Forwarded-User-Agent') ->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies') ->addHeader('Access-Control-Allow-Origin', $refDomain) ->addHeader('Access-Control-Allow-Credentials', 'true'); @@ -1110,7 +1104,7 @@ App::options() $response ->addHeader('Server', 'Appwrite') ->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE') - ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent') + ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Dev-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent') ->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies') ->addHeader('Access-Control-Allow-Origin', $origin) ->addHeader('Access-Control-Allow-Credentials', 'true') diff --git a/app/controllers/mock.php b/app/controllers/mock.php index 16d8e03841..fd7b9ab495 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -185,7 +185,7 @@ App::post('/v1/mock/api-key-unprefixed') Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'projectId' => $project->getId(), 'name' => 'Outdated key', 'scopes' => $scopes, @@ -235,7 +235,7 @@ App::get('/v1/mock/github/callback') $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); $owner = $github->getOwnerName($providerInstallationId) ?? ''; - $projectInternalId = $project->getInternalId(); + $projectInternalId = $project->getSequence(); $teamId = $project->getAttribute('teamId', ''); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 937f245099..f99ebbce07 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -574,7 +574,7 @@ App::init() $bucketId = $parts[1] ?? null; $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getInternalId(); + $isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence(); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); @@ -593,12 +593,12 @@ App::init() $fileId = $parts[1] ?? null; if ($fileSecurity && !$valid && !$isToken) { - $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); } else { - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); } - if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getInternalId()) { + if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) { throw new Exception(Exception::USER_UNAUTHORIZED); } diff --git a/app/http.php b/app/http.php index e413c5d248..30f4013821 100644 --- a/app/http.php +++ b/app/http.php @@ -15,9 +15,11 @@ use Utopia\Audit\Audit; use Utopia\CLI\Console; use Utopia\Compression\Compression; use Utopia\Config\Config; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; +use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -170,7 +172,7 @@ function createDatabase(App $app, string $resourceKey, string $dbName, array $co $sleep = 1; $attempts = 0; - do { + while (true) { try { $attempts++; $resource = $app->getResource($resourceKey); @@ -179,13 +181,12 @@ function createDatabase(App $app, string $resourceKey, string $dbName, array $co break; // exit loop on success } catch (\Exception $e) { Console::warning(" └── Database not ready. Retrying connection ({$attempts})..."); - $pools->reclaim(); if ($attempts >= $max) { throw new \Exception(' └── Failed to connect to database: ' . $e->getMessage()); } sleep($sleep); } - } while ($attempts < $max); + } Console::success("[Setup] - $dbName database init started..."); @@ -308,7 +309,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg 'orders' => $index['orders'], ]), $files['indexes']); - $dbForPlatform->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); + $dbForPlatform->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes); } if (Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')->isEmpty())) { @@ -356,7 +357,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg 'orders' => $index['orders'], ]), $files['indexes']); - Authorization::skip(fn () => $dbForPlatform->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes)); + Authorization::skip(fn () => $dbForPlatform->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes)); } }); @@ -368,11 +369,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg $cache = $app->getResource('cache'); foreach ($sharedTablesV2 as $hostname) { - $adapter = $pools - ->get($hostname) - ->pop() - ->getResource(); - + $adapter = new DatabasePool($pools->get($hostname)); $dbForProject = (new Database($adapter, $cache)) ->setDatabase('appwrite') ->setSharedTables(true) @@ -382,7 +379,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg try { Console::success('[Setup] - Creating project database: ' . $hostname . '...'); $dbForProject->create(); - } catch (Duplicate) { + } catch (DuplicateException) { Console::success('[Setup] - Skip: metadata table already exists'); } @@ -408,7 +405,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg } } - $pools->reclaim(); Console::success('[Setup] - Server database init completed...'); }); @@ -523,6 +519,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool Console::error('[Error] Message: ' . $th->getMessage()); Console::error('[Error] File: ' . $th->getFile()); Console::error('[Error] Line: ' . $th->getLine()); + Console::error('[Error] Trace: ' . $th->getTraceAsString()); $swooleResponse->setStatusCode(500); @@ -540,8 +537,6 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool ]; $swooleResponse->end(\json_encode($output)); - } finally { - $pools->reclaim(); } }); diff --git a/app/init/configs.php b/app/init/configs.php index 6cd57faa93..7572302919 100644 --- a/app/init/configs.php +++ b/app/init/configs.php @@ -2,6 +2,8 @@ use Utopia\Config\Config; +require_once __DIR__ . '/../config/storage/resource_limits.php'; + Config::load('template-runtimes', __DIR__ . '/../config/template-runtimes.php'); Config::load('events', __DIR__ . '/../config/events.php'); Config::load('auth', __DIR__ . '/../config/auth.php'); diff --git a/app/init/constants.php b/app/init/constants.php index 7686759719..ebf79086a7 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -37,8 +37,8 @@ const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours const APP_RESOURCE_TOKEN_ACCESS = 24 * 60 * 60; // 24 hours const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours -const APP_CACHE_BUSTER = 4319; -const APP_VERSION_STABLE = '1.7.0'; +const APP_CACHE_BUSTER = 4320; +const APP_VERSION_STABLE = '1.7.4'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; @@ -51,6 +51,7 @@ const APP_DATABASE_TIMEOUT_MILLISECONDS_API = 15 * 1000; // 15 seconds const APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER = 300 * 1000; // 5 minutes const APP_DATABASE_TIMEOUT_MILLISECONDS_TASK = 300 * 1000; // 5 minutes const APP_DATABASE_QUERY_MAX_VALUES = 500; +const APP_DATABASE_ENCRYPT_SIZE_MIN = 150; const APP_STORAGE_UPLOADS = '/storage/uploads'; const APP_STORAGE_SITES = '/storage/sites'; const APP_STORAGE_FUNCTIONS = '/storage/functions'; diff --git a/app/init/database/filters.php b/app/init/database/filters.php index f110fe1554..98a37ec4ad 100644 --- a/app/init/database/filters.php +++ b/app/init/database/filters.php @@ -71,18 +71,27 @@ Database::addFilter( }, function (mixed $value, Document $document, Database $database) { $attributes = $database->find('attributes', [ - Query::equal('collectionInternalId', [$document->getInternalId()]), + Query::equal('collectionInternalId', [$document->getSequence()]), Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), Query::limit($database->getLimitForAttributes()), ]); foreach ($attributes as $attribute) { - if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { - $options = $attribute->getAttribute('options'); - foreach ($options as $key => $value) { - $attribute->setAttribute($key, $value); - } - $attribute->removeAttribute('options'); + $attributeType = $attribute->getAttribute('type'); + + switch ($attributeType) { + case Database::VAR_RELATIONSHIP: + $options = $attribute->getAttribute('options'); + foreach ($options as $key => $value) { + $attribute->setAttribute($key, $value); + } + $attribute->removeAttribute('options'); + break; + + case Database::VAR_STRING: + $filters = $attribute->getAttribute('filters', []); + $attribute->setAttribute('encrypt', in_array('encrypt', $filters)); + break; } } @@ -98,7 +107,7 @@ Database::addFilter( function (mixed $value, Document $document, Database $database) { return $database ->find('indexes', [ - Query::equal('collectionInternalId', [$document->getInternalId()]), + Query::equal('collectionInternalId', [$document->getSequence()]), Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), Query::limit($database->getLimitForIndexes()), ]); @@ -113,7 +122,7 @@ Database::addFilter( function (mixed $value, Document $document, Database $database) { return $database ->find('platforms', [ - Query::equal('projectInternalId', [$document->getInternalId()]), + Query::equal('projectInternalId', [$document->getSequence()]), Query::limit(APP_LIMIT_SUBQUERY), ]); } @@ -127,7 +136,7 @@ Database::addFilter( function (mixed $value, Document $document, Database $database) { return $database ->find('keys', [ - Query::equal('projectInternalId', [$document->getInternalId()]), + Query::equal('projectInternalId', [$document->getSequence()]), Query::limit(APP_LIMIT_SUBQUERY), ]); } @@ -141,7 +150,7 @@ Database::addFilter( function (mixed $value, Document $document, Database $database) { return $database ->find('devKeys', [ - Query::equal('projectInternalId', [$document->getInternalId()]), + Query::equal('projectInternalId', [$document->getSequence()]), Query::limit(APP_LIMIT_SUBQUERY), ]); } @@ -155,7 +164,7 @@ Database::addFilter( function (mixed $value, Document $document, Database $database) { return $database ->find('webhooks', [ - Query::equal('projectInternalId', [$document->getInternalId()]), + Query::equal('projectInternalId', [$document->getSequence()]), Query::limit(APP_LIMIT_SUBQUERY), ]); } @@ -168,7 +177,7 @@ Database::addFilter( }, function (mixed $value, Document $document, Database $database) { return Authorization::skip(fn () => $database->find('sessions', [ - Query::equal('userInternalId', [$document->getInternalId()]), + Query::equal('userInternalId', [$document->getSequence()]), Query::limit(APP_LIMIT_SUBQUERY), ])); } @@ -182,7 +191,7 @@ Database::addFilter( function (mixed $value, Document $document, Database $database) { return Authorization::skip(fn () => $database ->find('tokens', [ - Query::equal('userInternalId', [$document->getInternalId()]), + Query::equal('userInternalId', [$document->getSequence()]), Query::limit(APP_LIMIT_SUBQUERY), ])); } @@ -196,7 +205,7 @@ Database::addFilter( function (mixed $value, Document $document, Database $database) { return Authorization::skip(fn () => $database ->find('challenges', [ - Query::equal('userInternalId', [$document->getInternalId()]), + Query::equal('userInternalId', [$document->getSequence()]), Query::limit(APP_LIMIT_SUBQUERY), ])); } @@ -210,7 +219,7 @@ Database::addFilter( function (mixed $value, Document $document, Database $database) { return Authorization::skip(fn () => $database ->find('authenticators', [ - Query::equal('userInternalId', [$document->getInternalId()]), + Query::equal('userInternalId', [$document->getSequence()]), Query::limit(APP_LIMIT_SUBQUERY), ])); } @@ -224,7 +233,7 @@ Database::addFilter( function (mixed $value, Document $document, Database $database) { return Authorization::skip(fn () => $database ->find('memberships', [ - Query::equal('userInternalId', [$document->getInternalId()]), + Query::equal('userInternalId', [$document->getSequence()]), Query::limit(APP_LIMIT_SUBQUERY), ])); } @@ -238,7 +247,7 @@ Database::addFilter( function (mixed $value, Document $document, Database $database) { return $database ->find('variables', [ - Query::equal('resourceInternalId', [$document->getInternalId()]), + Query::equal('resourceInternalId', [$document->getSequence()]), Query::equal('resourceType', ['function', 'site']), Query::limit(APP_LIMIT_SUBQUERY), ]); @@ -316,7 +325,7 @@ Database::addFilter( function (mixed $value, Document $document, Database $database) { return Authorization::skip(fn () => $database ->find('targets', [ - Query::equal('userInternalId', [$document->getInternalId()]), + Query::equal('userInternalId', [$document->getSequence()]), Query::limit(APP_LIMIT_SUBQUERY) ])); } @@ -331,13 +340,13 @@ Database::addFilter( $targetIds = Authorization::skip(fn () => \array_map( fn ($document) => $document->getAttribute('targetInternalId'), $database->find('subscribers', [ - Query::equal('topicInternalId', [$document->getInternalId()]), + Query::equal('topicInternalId', [$document->getSequence()]), Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY) ]) )); if (\count($targetIds) > 0) { return $database->skipValidation(fn () => $database->find('targets', [ - Query::equal('$internalId', $targetIds) + Query::equal('$sequence', $targetIds) ])); } return []; diff --git a/app/init/registers.php b/app/init/registers.php index 883c4f66bb..80b500c9a0 100644 --- a/app/init/registers.php +++ b/app/init/registers.php @@ -217,13 +217,13 @@ $register->set('pools', function () { 'mysql', 'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { - return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( + return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, [ \PDO::ATTR_TIMEOUT => 3, // Seconds \PDO::ATTR_PERSISTENT => false, \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, \PDO::ATTR_EMULATE_PREPARES => true, \PDO::ATTR_STRINGIFY_FETCHES => true - )); + ]); }); }, 'postgresql' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { diff --git a/app/init/resources.php b/app/init/resources.php index e96432ad70..838f4aa21b 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -24,10 +24,12 @@ use Appwrite\Utopia\Request; use Executor\Executor; use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis; use Utopia\App; +use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\DateTime as DatabaseDateTime; use Utopia\Database\Document; @@ -38,6 +40,7 @@ use Utopia\DSN\DSN; use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Pools\Group; +use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Publisher; use Utopia\Storage\Device; use Utopia\Storage\Device\AWS; @@ -74,10 +77,10 @@ App::setResource('localeCodes', function () { // Queues App::setResource('publisher', function (Group $pools) { - return $pools->get('publisher')->pop()->getResource(); + return new BrokerPool(publisher: $pools->get('publisher')); }, ['pools']); App::setResource('consumer', function (Group $pools) { - return $pools->get('consumer')->pop()->getResource(); + return new BrokerPool(consumer: $pools->get('consumer')); }, ['pools']); App::setResource('queueForMessaging', function (Publisher $publisher) { return new Messaging($publisher); @@ -331,12 +334,8 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform $dsn = new DSN('mysql://' . $project->getAttribute('database')); } - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); + $adapter = new DatabasePool($pools->get($dsn->getHost())); + $database = new Database($adapter, $cache); $database ->setMetadata('host', \gethostname()) @@ -349,25 +348,21 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform if (\in_array($dsn->getHost(), $sharedTables)) { $database ->setSharedTables(true) - ->setTenant($project->getInternalId()) + ->setTenant($project->getSequence()) ->setNamespace($dsn->getParam('namespace')); } else { $database ->setSharedTables(false) ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); + ->setNamespace('_' . $project->getSequence()); } return $database; }, ['pools', 'dbForPlatform', 'cache', 'project']); App::setResource('dbForPlatform', function (Group $pools, Cache $cache) { - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); + $adapter = new DatabasePool($pools->get('console')); + $database = new Database($adapter, $cache); $database ->setNamespace('_console') @@ -380,7 +375,7 @@ App::setResource('dbForPlatform', function (Group $pools, Cache $cache) { }, ['pools', 'cache']); App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) { - $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools + $databases = []; return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases) { if ($project->isEmpty() || $project->getId() === 'console') { @@ -406,13 +401,13 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform if (\in_array($dsn->getHost(), $sharedTables)) { $database ->setSharedTables(true) - ->setTenant($project->getInternalId()) + ->setTenant($project->getSequence()) ->setNamespace($dsn->getParam('namespace')); } else { $database ->setSharedTables(false) ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); + ->setNamespace('_' . $project->getSequence()); } }); @@ -422,12 +417,8 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform return $database; } - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); + $adapter = new DatabasePool($pools->get($dsn->getHost())); + $database = new Database($adapter, $cache); $databases[$dsn->getHost()] = $database; $configure($database); @@ -437,21 +428,15 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform App::setResource('getLogsDB', function (Group $pools, Cache $cache) { $database = null; - return function (?Document $project = null) use ($pools, $cache, $database) { + + return function (?Document $project = null) use ($pools, $cache, &$database) { if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') { - $database->setTenant($project->getInternalId()); + $database->setTenant($project->getSequence()); return $database; } - $dbAdapter = $pools - ->get('logs') - ->pop() - ->getResource(); - - $database = new Database( - $dbAdapter, - $cache - ); + $adapter = new DatabasePool($pools->get('logs')); + $database = new Database($adapter, $cache); $database ->setSharedTables(true) @@ -461,7 +446,7 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) { // set tenant if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') { - $database->setTenant($project->getInternalId()); + $database->setTenant($project->getSequence()); } return $database; @@ -470,19 +455,18 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) { App::setResource('telemetry', fn () => new NoTelemetry()); -App::setResource('cache', function (Group $pools) { +App::setResource('cache', function (Group $pools, Telemetry $telemetry) { $list = Config::getParam('pools-cache', []); $adapters = []; foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource(); + $adapters[] = new CachePool($pools->get($value)); } - return new Cache(new Sharding($adapters)); -}, ['pools']); + $cache = new Cache(new Sharding($adapters)); + $cache->setTelemetry($telemetry); + return $cache; +}, ['pools', 'telemetry']); App::setResource('redis', function () { $host = System::getEnv('_APP_REDIS_HOST', 'localhost'); @@ -506,24 +490,23 @@ App::setResource('timelimit', function (\Redis $redis) { }, ['redis']); App::setResource('deviceForLocal', function (Telemetry $telemetry) { - return new Local(); + return new Device\Telemetry($telemetry, new Local()); }, ['telemetry']); - -App::setResource('deviceForFiles', function ($project) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); -}, ['project']); -App::setResource('deviceForSites', function ($project) { - return getDevice(APP_STORAGE_SITES . '/app-' . $project->getId()); -}, ['project']); -App::setResource('deviceForImports', function ($project) { - return getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId()); -}, ['project']); -App::setResource('deviceForFunctions', function ($project) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); -}, ['project']); -App::setResource('deviceForBuilds', function ($project) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); -}, ['project']); +App::setResource('deviceForFiles', function ($project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); +App::setResource('deviceForSites', function ($project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_SITES . '/app-' . $project->getId())); +}, ['project', 'telemetry']); +App::setResource('deviceForImports', function ($project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); +App::setResource('deviceForFunctions', function ($project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); +App::setResource('deviceForBuilds', function ($project, Telemetry $telemetry) { + return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); function getDevice(string $root, string $connection = ''): Device { @@ -552,7 +535,8 @@ function getDevice(string $root, string $connection = ''): Device switch ($device) { case Storage::DEVICE_S3: if (!empty($url)) { - return new S3($root, $accessKey, $accessSecret, $url, $region, $acl); + $bucketRoot = (!empty($bucket) ? $bucket . '/' : '') . \ltrim($root, '/'); + return new S3($bucketRoot, $accessKey, $accessSecret, $url, $region, $acl); } else { return new AWS($root, $accessKey, $accessSecret, $bucket, $region, $acl); } @@ -584,7 +568,8 @@ function getDevice(string $root, string $connection = ''): Device $s3Acl = 'private'; $s3EndpointUrl = System::getEnv('_APP_STORAGE_S3_ENDPOINT', ''); if (!empty($s3EndpointUrl)) { - return new S3($root, $s3AccessKey, $s3SecretKey, $s3EndpointUrl, $s3Region, $s3Acl); + $bucketRoot = (!empty($s3Bucket) ? $s3Bucket . '/' : '') . \ltrim($root, '/'); + return new S3($bucketRoot, $s3AccessKey, $s3SecretKey, $s3EndpointUrl, $s3Region, $s3Acl); } else { return new AWS($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); } @@ -854,7 +839,7 @@ App::setResource('team', function (Document $project, Database $dbForPlatform, A $team = Authorization::skip(function () use ($dbForPlatform, $teamInternalId) { return $dbForPlatform->findOne('teams', [ - Query::equal('$internalId', [$teamInternalId]), + Query::equal('$sequence', [$teamInternalId]), ]); }); @@ -895,7 +880,7 @@ App::setResource('apiKey', function (Request $request, Document $project): ?Key return Key::decode($project, $key); }, ['request', 'project']); -App::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST'))); +App::setResource('executor', fn () => new Executor()); App::setResource('resourceToken', function ($project, $dbForProject, $request) { $tokenJWT = $request->getParam('token'); @@ -933,10 +918,10 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) { return match ($token->getAttribute('resourceType')) { TOKENS_RESOURCE_TYPE_FILES => (function () use ($token, $dbForProject) { - $internalIds = explode(':', $token->getAttribute('resourceInternalId')); + $sequences = explode(':', $token->getAttribute('resourceInternalId')); $ids = explode(':', $token->getAttribute('resourceId')); - if (count($internalIds) !== 2 || count($ids) !== 2) { + if (count($sequences) !== 2 || count($ids) !== 2) { return new Document([]); } @@ -949,8 +934,8 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) { return new Document([ 'bucketId' => $ids[0], 'fileId' => $ids[1], - 'bucketInternalId' => $internalIds[0], - 'fileInternalId' => $internalIds[1], + 'bucketInternalId' => $sequences[0], + 'fileInternalId' => $sequences[1], ]); })(), diff --git a/app/realtime.php b/app/realtime.php index 86f9c85fdd..96484c8a35 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -5,6 +5,7 @@ use Appwrite\Extend\Exception; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Network\Validator\Origin; +use Appwrite\PubSub\Adapter\Pool as PubSubPool; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Swoole\Http\Request as SwooleRequest; @@ -15,10 +16,12 @@ use Swoole\Timer; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis; use Utopia\App; +use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -28,13 +31,15 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\DSN\DSN; use Utopia\Logger\Log; +use Utopia\Pools\Group; +use Utopia\Registry\Registry; use Utopia\System\System; use Utopia\Telemetry\Adapter\None as NoTelemetry; use Utopia\WebSocket\Adapter; use Utopia\WebSocket\Server; /** - * @var \Utopia\Registry\Registry $register + * @var Registry $register */ require_once __DIR__ . '/init.php'; @@ -46,17 +51,17 @@ if (!function_exists('getConsoleDB')) { { global $register; - /** @var \Utopia\Pools\Group $pools */ + static $database = null; + + if ($database !== null) { + return $database; + } + + /** @var Group $pools */ $pools = $register->get('pools'); - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource() - ; - - $database = new Database($dbAdapter, getCache()); - + $adapter = new DatabasePool($pools->get('console')); + $database = new Database($adapter, getCache()); $database ->setNamespace('_console') ->setMetadata('host', \gethostname()) @@ -72,7 +77,13 @@ if (!function_exists('getProjectDB')) { { global $register; - /** @var \Utopia\Pools\Group $pools */ + static $databases = []; + + if (isset($databases[$project->getSequence()])) { + return $databases[$project->getSequence()]; + } + + /** @var Group $pools */ $pools = $register->get('pools'); if ($project->isEmpty() || $project->getId() === 'console') { @@ -86,11 +97,7 @@ if (!function_exists('getProjectDB')) { $dsn = new DSN('mysql://' . $project->getAttribute('database')); } - $adapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - + $adapter = new DatabasePool($pools->get($dsn->getHost())); $database = new Database($adapter, getCache()); $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); @@ -98,20 +105,20 @@ if (!function_exists('getProjectDB')) { if (\in_array($dsn->getHost(), $sharedTables)) { $database ->setSharedTables(true) - ->setTenant($project->getInternalId()) + ->setTenant($project->getSequence()) ->setNamespace($dsn->getParam('namespace')); } else { $database ->setSharedTables(false) ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); + ->setNamespace('_' . $project->getSequence()); } $database ->setMetadata('host', \gethostname()) ->setMetadata('project', $project->getId()); - return $database; + return $databases[$project->getSequence()] = $database; } } @@ -121,20 +128,22 @@ if (!function_exists('getCache')) { { global $register; - $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ + static $cache = null; + + if ($cache !== null) { + return $cache; + } + + $pools = $register->get('pools'); /** @var Group $pools */ $list = Config::getParam('pools-cache', []); $adapters = []; foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource() - ; + $adapters[] = new CachePool($pools->get($value)); } - return new Cache(new Sharding($adapters)); + return $cache = new Cache(new Sharding($adapters)); } } @@ -142,6 +151,12 @@ if (!function_exists('getCache')) { if (!function_exists('getRedis')) { function getRedis(): \Redis { + static $redis = null; + + if ($redis !== null) { + return $redis; + } + $host = System::getEnv('_APP_REDIS_HOST', 'localhost'); $port = System::getEnv('_APP_REDIS_PORT', 6379); $pass = System::getEnv('_APP_REDIS_PASS', ''); @@ -160,21 +175,39 @@ if (!function_exists('getRedis')) { if (!function_exists('getTimelimit')) { function getTimelimit(): TimeLimitRedis { - return new TimeLimitRedis("", 0, 1, getRedis()); + static $timelimit = null; + + if ($timelimit !== null) { + return $timelimit; + } + + return $timelimit = new TimeLimitRedis("", 0, 1, getRedis()); } } if (!function_exists('getRealtime')) { function getRealtime(): Realtime { - return new Realtime(); + static $realtime = null; + + if ($realtime !== null) { + return $realtime; + } + + return $realtime = new Realtime(); } } if (!function_exists('getTelemetry')) { function getTelemetry(int $workerId): Utopia\Telemetry\Adapter { - return new NoTelemetry(); + static $telemetry = null; + + if ($telemetry !== null) { + return $telemetry; + } + + return $telemetry = new NoTelemetry(); } } @@ -273,7 +306,6 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume sleep(DATABASE_RECONNECT_SLEEP); } } while (true); - $register->get('pools')->reclaim(); }); /** @@ -299,9 +331,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume Authorization::skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument)); } catch (Throwable $th) { - call_user_func($logError, $th, "updateWorkerDocument"); - } finally { - $register->get('pools')->reclaim(); + $logError($th, "updateWorkerDocument"); } }); } @@ -370,8 +400,6 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, 'data' => $event['data'] ])); } - - $register->get('pools')->reclaim(); } } /** @@ -407,8 +435,8 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, } $start = time(); - /** @var \Appwrite\PubSub\Adapter $pubsub */ - $pubsub = $register->get('pools')->get('pubsub')->pop()->getResource(); + $pubsub = new PubSubPool($register->get('pools')->get('pubsub')); + if ($pubsub->ping(true)) { $attempts = 0; Console::success('Pub/sub connection established (worker: ' . $workerId . ')'); @@ -436,8 +464,6 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $realtime->unsubscribe($connection); $realtime->subscribe($projectId, $connection, $roles, $channels); - - $register->get('pools')->reclaim(); } } @@ -463,14 +489,12 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, } }); } catch (Throwable $th) { - call_user_func($logError, $th, "pubSubConnection"); + $logError($th, "pubSubConnection"); Console::error('Pub/sub error: ' . $th->getMessage()); $attempts++; sleep(DATABASE_RECONNECT_SLEEP); continue; - } finally { - $register->get('pools')->reclaim(); } } @@ -572,7 +596,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $stats->incr($project->getId(), 'connections'); $stats->incr($project->getId(), 'connectionsTotal'); } catch (Throwable $th) { - call_user_func($logError, $th, "initServer"); + $logError($th, "initServer"); // Handle SQL error code is 'HY000' $code = $th->getCode(); @@ -596,8 +620,6 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, Console::error('[Error] Code: ' . $response['data']['code']); Console::error('[Error] Message: ' . $response['data']['message']); } - } finally { - $register->get('pools')->reclaim(); } }); @@ -696,8 +718,6 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re if ($th->getCode() === 1008) { $server->close($connection, $th->getCode()); } - } finally { - $register->get('pools')->reclaim(); } }); diff --git a/app/views/general/404.phtml b/app/views/general/404.phtml index 5e63344c8a..2a9ff7f312 100644 --- a/app/views/general/404.phtml +++ b/app/views/general/404.phtml @@ -151,37 +151,6 @@ - -
-

Powered by

- - - - - - - - - - - - - - - - - - - - - - - - - - - -
\ No newline at end of file diff --git a/app/views/general/error.phtml b/app/views/general/error.phtml index c6457ce98f..a427f1d5e8 100644 --- a/app/views/general/error.phtml +++ b/app/views/general/error.phtml @@ -479,67 +479,39 @@ switch ($type) { -
- -
Error trace
- $traceItem): ?> -
- -
file
-
print($traceItem['file']); ?>
- + +
+ +
Error trace
+ $traceItem): ?> +
+ +
file
+
print($traceItem['file']); ?>
+ - -
line
-
print($traceItem['line']); ?>
- + +
line
+
print($traceItem['line']); ?>
+ - -
function
-
print($traceItem['function']); ?>
- + +
function
+
print($traceItem['function']); ?>
+ - -
args
-
print(\var_export($traceItem['args'], true)); ?>
- -
- -
+ +
args
+
print(\var_export($traceItem['args'], true)); ?>
+ +
+ +
+ -
-

Powered by

- - - - - - - - - - - - - - - - - - - - - - - - - - - -
\ No newline at end of file diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index afeab4033e..24fe5c9337 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -179,7 +179,7 @@ $dbService = $this->getParam('database'); appwrite-console: <<: *x-logging container_name: appwrite-console - image: /console:6.0.1 + image: /console:6.0.13 restart: unless-stopped networks: - appwrite @@ -887,7 +887,7 @@ $dbService = $this->getParam('database'); <<: *x-logging restart: unless-stopped stop_signal: SIGINT - image: openruntimes/executor:0.7.14 + image: openruntimes/executor:0.7.20 networks: - appwrite - runtimes diff --git a/app/worker.php b/app/worker.php index 0dceb4f937..248989aa1c 100644 --- a/app/worker.php +++ b/app/worker.php @@ -18,12 +18,16 @@ use Appwrite\Event\StatsUsage; use Appwrite\Event\Webhook; use Appwrite\Platform\Appwrite; use Executor\Executor; +use Swoole\Process; use Swoole\Runtime; +use Swoole\Timer; use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis; +use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -33,29 +37,28 @@ use Utopia\Logger\Log; use Utopia\Logger\Logger; use Utopia\Platform\Service; use Utopia\Pools\Group; +use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Queue\Message; use Utopia\Queue\Publisher; use Utopia\Queue\Server; use Utopia\Registry\Registry; +use Utopia\Storage\Device\Telemetry as TelemetryDevice; use Utopia\System\System; +use Utopia\Telemetry\Adapter as Telemetry; use Utopia\Telemetry\Adapter\None as NoTelemetry; Authorization::disable(); -Runtime::enableCoroutine(SWOOLE_HOOK_ALL); +Runtime::enableCoroutine(); Server::setResource('register', fn () => $register); Server::setResource('dbForPlatform', function (Cache $cache, Registry $register) { $pools = $register->get('pools'); - $database = $pools - ->get('console') - ->pop() - ->getResource(); + $adapter = new DatabasePool($pools->get('console')); + $dbForPlatform = new Database($adapter, $cache); + $dbForPlatform->setNamespace('_console'); - $adapter = new Database($database, $cache); - $adapter->setNamespace('_console'); - - return $adapter; + return $dbForPlatform; }, ['cache', 'register']); Server::setResource('project', function (Message $message, Database $dbForPlatform) { @@ -83,32 +86,21 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register, $dsn = new DSN('mysql://' . $project->getAttribute('database')); } - $adapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - + $adapter = new DatabasePool($pools->get($dsn->getHost())); $database = new Database($adapter, $cache); - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); if (\in_array($dsn->getHost(), $sharedTables)) { $database ->setSharedTables(true) - ->setTenant($project->getInternalId()) + ->setTenant($project->getSequence()) ->setNamespace($dsn->getParam('namespace')); } else { $database ->setSharedTables(false) ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); + ->setNamespace('_' . $project->getSequence()); } $database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER); @@ -139,24 +131,20 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf if (\in_array($dsn->getHost(), $sharedTables)) { $database ->setSharedTables(true) - ->setTenant($project->getInternalId()) + ->setTenant($project->getSequence()) ->setNamespace($dsn->getParam('namespace')); } else { $database ->setSharedTables(false) ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); + ->setNamespace('_' . $project->getSequence()); } return $database; } - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); + $adapter = new DatabasePool($pools->get($dsn->getHost())); + $database = new Database($adapter, $cache); $databases[$dsn->getHost()] = $database; @@ -165,13 +153,13 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf if (\in_array($dsn->getHost(), $sharedTables)) { $database ->setSharedTables(true) - ->setTenant($project->getInternalId()) + ->setTenant($project->getSequence()) ->setNamespace($dsn->getParam('namespace')); } else { $database ->setSharedTables(false) ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); + ->setNamespace('_' . $project->getSequence()); } $database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER); @@ -184,19 +172,12 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) { $database = null; return function (?Document $project = null) use ($pools, $cache, $database) { if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') { - $database->setTenant($project->getInternalId()); + $database->setTenant($project->getSequence()); return $database; } - $dbAdapter = $pools - ->get('logs') - ->pop() - ->getResource(); - - $database = new Database( - $dbAdapter, - $cache - ); + $adapter = new DatabasePool($pools->get('logs')); + $database = new Database($adapter, $cache); $database ->setSharedTables(true) @@ -206,7 +187,7 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) { // set tenant if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') { - $database->setTenant($project->getInternalId()); + $database->setTenant($project->getSequence()); } return $database; @@ -234,11 +215,7 @@ Server::setResource('cache', function (Registry $register) { $adapters = []; foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource() - ; + $adapters[] = new CachePool($pools->get($value)); } return new Cache(new Sharding($adapters)); @@ -267,12 +244,13 @@ Server::setResource('timelimit', function (\Redis $redis) { Server::setResource('log', fn () => new Log()); + Server::setResource('publisher', function (Group $pools) { - return $pools->get('publisher')->pop()->getResource(); + return new BrokerPool(publisher: $pools->get('publisher')); }, ['pools']); Server::setResource('consumer', function (Group $pools) { - return $pools->get('consumer')->pop()->getResource(); + return new BrokerPool(consumer: $pools->get('consumer')); }, ['pools']); Server::setResource('queueForStatsUsage', function (Publisher $publisher) { @@ -337,29 +315,29 @@ Server::setResource('pools', function (Registry $register) { Server::setResource('telemetry', fn () => new NoTelemetry()); -Server::setResource('deviceForSites', function (Document $project) { - return getDevice(APP_STORAGE_SITES . '/app-' . $project->getId()); -}, ['project']); +Server::setResource('deviceForSites', function (Document $project, Telemetry $telemetry) { + return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_SITES . '/app-' . $project->getId())); +}, ['project', 'telemetry']); -Server::setResource('deviceForImports', function (Document $project) { - return getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId()); -}, ['project']); +Server::setResource('deviceForImports', function (Document $project, Telemetry $telemetry) { + return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); -Server::setResource('deviceForFunctions', function (Document $project) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); -}, ['project']); +Server::setResource('deviceForFunctions', function (Document $project, Telemetry $telemetry) { + return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); -Server::setResource('deviceForFiles', function (Document $project) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); -}, ['project']); +Server::setResource('deviceForFiles', function (Document $project, Telemetry $telemetry) { + return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); -Server::setResource('deviceForBuilds', function (Document $project) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); -}, ['project']); +Server::setResource('deviceForBuilds', function (Document $project, Telemetry $telemetry) { + return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId())); +}, ['project', 'telemetry']); -Server::setResource('deviceForCache', function (Document $project) { - return getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId()); -}, ['project']); +Server::setResource('deviceForCache', function (Document $project, Telemetry $telemetry) { + return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId())); +}, ['project', 'telemetry']); Server::setResource( 'isResourceBlocked', @@ -424,7 +402,7 @@ Server::setResource('logError', function (Registry $register, Document $project) }; }, ['register', 'project']); -Server::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST'))); +Server::setResource('executor', fn () => new Executor()); $pools = $register->get('pools'); $platform = new Appwrite(); @@ -463,13 +441,6 @@ try { $worker = $platform->getWorker(); -$worker - ->shutdown() - ->inject('pools') - ->action(function (Group $pools) { - $pools->reclaim(); - }); - $worker ->error() ->inject('error') @@ -477,8 +448,7 @@ $worker ->inject('log') ->inject('pools') ->inject('project') - ->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($queueName) { - $pools->reclaim(); + ->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($worker, $queueName) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); if ($logger) { @@ -514,8 +484,15 @@ $worker }); $worker->workerStart() - ->action(function () use ($workerName) { - Console::info("Worker $workerName started"); + ->action(function () use ($worker, $workerName) { + Console::info("Worker $workerName started"); + + Process::signal(SIGTERM, function () use ($worker, $workerName) { + Console::info("Stopping worker $workerName."); + + $worker->stop(); + Timer::clearAll(); + }); }); $worker->start(); diff --git a/bin/doctor b/bin/doctor index b2a4547156..92b3cf6bf8 100755 --- a/bin/doctor +++ b/bin/doctor @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php doctor $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php doctor $@ diff --git a/bin/install b/bin/install index e669e91e6b..126d5373bf 100755 --- a/bin/install +++ b/bin/install @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php install $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php install $@ diff --git a/bin/maintenance b/bin/maintenance index 099551cb32..70273feb61 100644 --- a/bin/maintenance +++ b/bin/maintenance @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php maintenance $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php maintenance $@ diff --git a/bin/migrate b/bin/migrate index 28ebbd19e7..32bf7ee2f4 100755 --- a/bin/migrate +++ b/bin/migrate @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php migrate $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php migrate $@ diff --git a/bin/queue-count-failed b/bin/queue-count-failed index ca8f2b4291..93a6b3136c 100644 --- a/bin/queue-count-failed +++ b/bin/queue-count-failed @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php queue-count --type=failed $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php queue-count --type=failed $@ diff --git a/bin/queue-count-processing b/bin/queue-count-processing index 325d86111d..18e89664bd 100644 --- a/bin/queue-count-processing +++ b/bin/queue-count-processing @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php queue-count --type=processing $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php queue-count --type=processing $@ diff --git a/bin/queue-count-success b/bin/queue-count-success index 34fc54b4c1..b38bfa2159 100644 --- a/bin/queue-count-success +++ b/bin/queue-count-success @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php queue-count --type=success $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php queue-count --type=success $@ diff --git a/bin/queue-retry b/bin/queue-retry index f9473e6b07..2224a66b3b 100644 --- a/bin/queue-retry +++ b/bin/queue-retry @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php queue-retry $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php queue-retry $@ diff --git a/bin/realtime b/bin/realtime index e43dc269e0..debd4baf2a 100644 --- a/bin/realtime +++ b/bin/realtime @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/realtime.php $@ \ No newline at end of file +exec php /usr/src/code/app/realtime.php $@ diff --git a/bin/schedule-executions b/bin/schedule-executions index f239cad206..5d503e374c 100644 --- a/bin/schedule-executions +++ b/bin/schedule-executions @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php schedule-executions $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php schedule-executions $@ diff --git a/bin/schedule-functions b/bin/schedule-functions index 10edbe8226..beca1a0420 100644 --- a/bin/schedule-functions +++ b/bin/schedule-functions @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php schedule-functions $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php schedule-functions $@ diff --git a/bin/schedule-messages b/bin/schedule-messages index fa7219f6ea..3f17a279b5 100644 --- a/bin/schedule-messages +++ b/bin/schedule-messages @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php schedule-messages $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php schedule-messages $@ diff --git a/bin/screenshot b/bin/screenshot index 4d8ceb998f..aef15eb96f 100755 --- a/bin/screenshot +++ b/bin/screenshot @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php screenshot $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php screenshot $@ \ No newline at end of file diff --git a/bin/sdks b/bin/sdks index ab73414829..3180813ea1 100644 --- a/bin/sdks +++ b/bin/sdks @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php sdks $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php sdks $@ diff --git a/bin/specs b/bin/specs index e77d1487d4..52875a1675 100644 --- a/bin/specs +++ b/bin/specs @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php specs $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php specs $@ diff --git a/bin/ssl b/bin/ssl index 83dcf6a026..0cc12375d0 100755 --- a/bin/ssl +++ b/bin/ssl @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php ssl $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php ssl $@ diff --git a/bin/stats-resources b/bin/stats-resources index 3104bab896..9cc67fb4a6 100644 --- a/bin/stats-resources +++ b/bin/stats-resources @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php stats-resources $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php stats-resources $@ diff --git a/bin/test b/bin/test index a2153fc536..c3b0d9b74c 100755 --- a/bin/test +++ b/bin/test @@ -1,3 +1,3 @@ #!/bin/sh -/usr/src/code/vendor/bin/phpunit --configuration /usr/src/code/phpunit.xml $@ \ No newline at end of file +exec /usr/src/code/vendor/bin/phpunit --configuration /usr/src/code/phpunit.xml $@ diff --git a/bin/upgrade b/bin/upgrade index ce32b9ca30..fd1f35b65a 100755 --- a/bin/upgrade +++ b/bin/upgrade @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php upgrade $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php upgrade $@ diff --git a/bin/vars b/bin/vars index 19e3f1ebf2..238d004675 100644 --- a/bin/vars +++ b/bin/vars @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/cli.php vars $@ \ No newline at end of file +exec php /usr/src/code/app/cli.php vars $@ diff --git a/bin/worker-audits b/bin/worker-audits index 3df65d65e8..9bf81b93e2 100644 --- a/bin/worker-audits +++ b/bin/worker-audits @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/worker.php audits $@ \ No newline at end of file +exec php /usr/src/code/app/worker.php audits $@ diff --git a/bin/worker-builds b/bin/worker-builds index 3400111cb5..7ddf5792de 100644 --- a/bin/worker-builds +++ b/bin/worker-builds @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/worker.php builds $@ \ No newline at end of file +exec php /usr/src/code/app/worker.php builds $@ diff --git a/bin/worker-certificates b/bin/worker-certificates index 901688c4c8..958627bc33 100755 --- a/bin/worker-certificates +++ b/bin/worker-certificates @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/worker.php certificates $@ \ No newline at end of file +exec php /usr/src/code/app/worker.php certificates $@ diff --git a/bin/worker-databases b/bin/worker-databases index 61e09aa9f1..235c16927c 100644 --- a/bin/worker-databases +++ b/bin/worker-databases @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/worker.php databases $@ +exec php /usr/src/code/app/worker.php databases $@ diff --git a/bin/worker-deletes b/bin/worker-deletes index 7c9793e6cb..c9711a4e66 100644 --- a/bin/worker-deletes +++ b/bin/worker-deletes @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/worker.php deletes $@ \ No newline at end of file +exec php /usr/src/code/app/worker.php deletes $@ diff --git a/bin/worker-functions b/bin/worker-functions index 4757b1b72a..e82506d0e2 100644 --- a/bin/worker-functions +++ b/bin/worker-functions @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/worker.php functions $@ \ No newline at end of file +exec php /usr/src/code/app/worker.php functions $@ diff --git a/bin/worker-mails b/bin/worker-mails index fee8a96da7..ffb4f7ea77 100644 --- a/bin/worker-mails +++ b/bin/worker-mails @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/worker.php mails $@ \ No newline at end of file +exec php /usr/src/code/app/worker.php mails $@ diff --git a/bin/worker-messaging b/bin/worker-messaging index e6edf80f06..6f39348419 100644 --- a/bin/worker-messaging +++ b/bin/worker-messaging @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/worker.php messaging $@ \ No newline at end of file +exec php /usr/src/code/app/worker.php messaging $@ diff --git a/bin/worker-migrations b/bin/worker-migrations index 32d4aef468..2728d9d22c 100644 --- a/bin/worker-migrations +++ b/bin/worker-migrations @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/worker.php migrations $@ \ No newline at end of file +exec php /usr/src/code/app/worker.php migrations $@ diff --git a/bin/worker-stats-resources b/bin/worker-stats-resources index 9c5d2bebff..70bfacbe52 100644 --- a/bin/worker-stats-resources +++ b/bin/worker-stats-resources @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/worker.php stats-resources $@ \ No newline at end of file +exec php /usr/src/code/app/worker.php stats-resources $@ diff --git a/bin/worker-stats-usage b/bin/worker-stats-usage index 2c267d805e..e6dd849a48 100644 --- a/bin/worker-stats-usage +++ b/bin/worker-stats-usage @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/worker.php stats-usage $@ \ No newline at end of file +exec php /usr/src/code/app/worker.php stats-usage $@ diff --git a/bin/worker-webhooks b/bin/worker-webhooks index 93f8027a81..63fc13e3c5 100644 --- a/bin/worker-webhooks +++ b/bin/worker-webhooks @@ -1,3 +1,3 @@ #!/bin/sh -php /usr/src/code/app/worker.php webhooks $@ \ No newline at end of file +exec php /usr/src/code/app/worker.php webhooks $@ diff --git a/composer.json b/composer.json index 7e445cd36b..18507e04f1 100644 --- a/composer.json +++ b/composer.json @@ -52,22 +52,22 @@ "utopia-php/cache": "0.13.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", + "utopia-php/database": "0.71.*", "utopia-php/detector": "0.1.*", - "utopia-php/database": "0.69.*", - "utopia-php/domains": "0.8.0", + "utopia-php/domains": "0.8.*", "utopia-php/dsn": "0.2.1", "utopia-php/framework": "0.33.*", "utopia-php/fetch": "0.4.*", "utopia-php/image": "0.8.*", "utopia-php/locale": "0.4.*", "utopia-php/logger": "0.6.*", - "utopia-php/messaging": "0.17.*", - "utopia-php/migration": "0.9.*", + "utopia-php/messaging": "0.18.*", + "utopia-php/migration": "0.10.*", "utopia-php/orchestration": "0.9.*", "utopia-php/platform": "0.7.*", "utopia-php/pools": "0.8.*", "utopia-php/preloader": "0.2.*", - "utopia-php/queue": "0.10.*", + "utopia-php/queue": "0.11.*", "utopia-php/registry": "0.5.*", "utopia-php/storage": "0.18.*", "utopia-php/swoole": "0.8.*", @@ -76,17 +76,17 @@ "utopia-php/vcs": "0.10.*", "utopia-php/websocket": "0.3.*", "matomo/device-detector": "6.1.*", - "dragonmantank/cron-expression": "3.3.2", - "phpmailer/phpmailer": "6.9.1", - "chillerlan/php-qrcode": "4.3.4", - "adhocore/jwt": "1.1.2", + "dragonmantank/cron-expression": "3.3.*", + "phpmailer/phpmailer": "6.9.*", + "chillerlan/php-qrcode": "4.3.*", + "adhocore/jwt": "1.1.*", "spomky-labs/otphp": "^10.0", "webonyx/graphql-php": "14.11.*", "league/csv": "9.14.*" }, "require-dev": { "ext-fileinfo": "*", - "appwrite/sdk-generator": "0.40.*", + "appwrite/sdk-generator": "0.41.*", "phpunit/phpunit": "9.*", "swoole/ide-helper": "5.1.2", "phpstan/phpstan": "1.8.*", diff --git a/composer.lock b/composer.lock index c058f036fc..6372df41ba 100644 --- a/composer.lock +++ b/composer.lock @@ -4,27 +4,27 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9f5de64d73e2ef73d796fa64f2baf232", + "content-hash": "1557e469b3074a6478a0b2fd522e1a2a", "packages": [ { "name": "adhocore/jwt", - "version": "1.1.2", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/adhocore/php-jwt.git", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899" + "reference": "ad417603d9d45578b6af2089ad5b78f101c82367" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/6c434af7170090bb7a8880d2bc220a2254ba7899", - "reference": "6c434af7170090bb7a8880d2bc220a2254ba7899", + "url": "https://api.github.com/repos/adhocore/php-jwt/zipball/ad417603d9d45578b6af2089ad5b78f101c82367", + "reference": "ad417603d9d45578b6af2089ad5b78f101c82367", "shasum": "" }, "require": { "php": "^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^6.5 || ^7.5" + "phpunit/phpunit": "^7.5 || ^8.5" }, "type": "library", "autoload": { @@ -53,15 +53,19 @@ ], "support": { "issues": "https://github.com/adhocore/php-jwt/issues", - "source": "https://github.com/adhocore/php-jwt/tree/1.1.2" + "source": "https://github.com/adhocore/php-jwt/tree/1.1.3" }, "funding": [ { "url": "https://paypal.me/ji10", "type": "custom" + }, + { + "url": "https://github.com/adhocore", + "type": "github" } ], - "time": "2021-02-20T09:56:44+00:00" + "time": "2025-02-18T01:00:50+00:00" }, { "name": "appwrite/appwrite", @@ -157,16 +161,16 @@ }, { "name": "appwrite/php-runtimes", - "version": "0.19.0", + "version": "0.19.1", "source": { "type": "git", "url": "https://github.com/appwrite/runtimes.git", - "reference": "8d21483efc19b9d977e323188989ee67a188464b" + "reference": "7bd0cc3cb97de625d7b07230bd91b121f88e72ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/runtimes/zipball/8d21483efc19b9d977e323188989ee67a188464b", - "reference": "8d21483efc19b9d977e323188989ee67a188464b", + "url": "https://api.github.com/repos/appwrite/runtimes/zipball/7bd0cc3cb97de625d7b07230bd91b121f88e72ae", + "reference": "7bd0cc3cb97de625d7b07230bd91b121f88e72ae", "shasum": "" }, "require": { @@ -206,9 +210,9 @@ ], "support": { "issues": "https://github.com/appwrite/runtimes/issues", - "source": "https://github.com/appwrite/runtimes/tree/0.19.0" + "source": "https://github.com/appwrite/runtimes/tree/0.19.1" }, - "time": "2025-03-25T22:37:51+00:00" + "time": "2025-05-27T07:12:56+00:00" }, { "name": "beberlei/assert", @@ -279,16 +283,16 @@ }, { "name": "brick/math", - "version": "0.12.3", + "version": "0.13.1", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" + "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", + "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", + "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04", "shasum": "" }, "require": { @@ -327,7 +331,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.3" + "source": "https://github.com/brick/math/tree/0.13.1" }, "funding": [ { @@ -335,7 +339,7 @@ "type": "github" } ], - "time": "2025-02-28T13:11:00+00:00" + "time": "2025-03-29T13:50:30+00:00" }, { "name": "chillerlan/php-qrcode", @@ -565,16 +569,16 @@ }, { "name": "dragonmantank/cron-expression", - "version": "v3.3.2", + "version": "v3.3.3", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", "shasum": "" }, "require": { @@ -614,7 +618,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3" }, "funding": [ { @@ -622,7 +626,7 @@ "type": "github" } ], - "time": "2022-09-10T18:51:20+00:00" + "time": "2023-08-10T19:36:49+00:00" }, { "name": "giggsey/libphonenumber-for-php-lite", @@ -709,16 +713,16 @@ }, { "name": "google/protobuf", - "version": "v4.31.0", + "version": "v4.31.1", "source": { "type": "git", "url": "https://github.com/protocolbuffers/protobuf-php.git", - "reference": "d59e31ce4bf0e4b48728e90c4d880839edb5be07" + "reference": "2b028ce8876254e2acbeceea7d9b573faad41864" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/d59e31ce4bf0e4b48728e90c4d880839edb5be07", - "reference": "d59e31ce4bf0e4b48728e90c4d880839edb5be07", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/2b028ce8876254e2acbeceea7d9b573faad41864", + "reference": "2b028ce8876254e2acbeceea7d9b573faad41864", "shasum": "" }, "require": { @@ -747,9 +751,9 @@ "proto" ], "support": { - "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.31.0" + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.31.1" }, - "time": "2025-05-14T16:17:23+00:00" + "time": "2025-05-28T18:52:35+00:00" }, { "name": "league/csv", @@ -1238,16 +1242,16 @@ }, { "name": "open-telemetry/exporter-otlp", - "version": "1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/exporter-otlp.git", - "reference": "19adf03d2b0f91f9e9b1c7f93db6c755c737cf6c" + "reference": "8b3ca1f86d01429c73b407bf1a2075d9c187001e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/19adf03d2b0f91f9e9b1c7f93db6c755c737cf6c", - "reference": "19adf03d2b0f91f9e9b1c7f93db6c755c737cf6c", + "url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/8b3ca1f86d01429c73b407bf1a2075d9c187001e", + "reference": "8b3ca1f86d01429c73b407bf1a2075d9c187001e", "shasum": "" }, "require": { @@ -1298,7 +1302,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-05-12T00:36:35+00:00" + "time": "2025-05-21T12:02:20+00:00" }, { "name": "open-telemetry/gen-otlp-protobuf", @@ -1365,16 +1369,16 @@ }, { "name": "open-telemetry/sdk", - "version": "1.4.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sdk.git", - "reference": "939d3a28395c249a763676458140dad44b3a8011" + "reference": "cd0d7367599717fc29e04eb8838ec061e6c2c657" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/939d3a28395c249a763676458140dad44b3a8011", - "reference": "939d3a28395c249a763676458140dad44b3a8011", + "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/cd0d7367599717fc29e04eb8838ec061e6c2c657", + "reference": "cd0d7367599717fc29e04eb8838ec061e6c2c657", "shasum": "" }, "require": { @@ -1451,7 +1455,7 @@ "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-05-07T12:32:21+00:00" + "time": "2025-05-22T02:33:34+00:00" }, { "name": "open-telemetry/sem-conv", @@ -2319,20 +2323,20 @@ }, { "name": "ramsey/uuid", - "version": "4.7.6", + "version": "4.8.1", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "91039bc1faa45ba123c4328958e620d382ec7088" + "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", - "reference": "91039bc1faa45ba123c4328958e620d382ec7088", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28", + "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", "ext-json": "*", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" @@ -2341,26 +2345,23 @@ "rhumsaa/uuid": "self.version" }, "require-dev": { - "captainhook/captainhook": "^5.10", + "captainhook/captainhook": "^5.25", "captainhook/plugin-composer": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "doctrine/annotations": "^1.8", - "ergebnis/composer-normalize": "^2.15", - "mockery/mockery": "^1.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", "paragonie/random-lib": "^2", - "php-mock/php-mock": "^2.2", - "php-mock/php-mock-mockery": "^1.3", - "php-parallel-lint/php-parallel-lint": "^1.1", - "phpbench/phpbench": "^1.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^8.5 || ^9", - "ramsey/composer-repl": "^1.4", - "slevomat/coding-standard": "^8.4", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.9" + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" }, "suggest": { "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", @@ -2395,19 +2396,9 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.7.6" + "source": "https://github.com/ramsey/uuid/tree/4.8.1" }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", - "type": "tidelift" - } - ], - "time": "2024-04-27T21:32:50+00:00" + "time": "2025-06-01T06:28:46+00:00" }, { "name": "spomky-labs/otphp", @@ -2486,16 +2477,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", "shasum": "" }, "require": { @@ -2508,7 +2499,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -2533,7 +2524,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, "funding": [ { @@ -2549,20 +2540,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/http-client", - "version": "v7.2.4", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "78981a2ffef6437ed92d4d7e2a86a82f256c6dc6" + "reference": "57e4fb86314015a695a750ace358d07a7e37b8a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/78981a2ffef6437ed92d4d7e2a86a82f256c6dc6", - "reference": "78981a2ffef6437ed92d4d7e2a86a82f256c6dc6", + "url": "https://api.github.com/repos/symfony/http-client/zipball/57e4fb86314015a695a750ace358d07a7e37b8a9", + "reference": "57e4fb86314015a695a750ace358d07a7e37b8a9", "shasum": "" }, "require": { @@ -2628,7 +2619,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.2.4" + "source": "https://github.com/symfony/http-client/tree/v7.3.0" }, "funding": [ { @@ -2644,20 +2635,20 @@ "type": "tidelift" } ], - "time": "2025-02-13T10:27:23+00:00" + "time": "2025-05-02T08:23:16+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v3.5.2", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645" + "reference": "75d7043853a42837e68111812f4d964b01e5101c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ee8d807ab20fcb51267fdace50fbe3494c31e645", - "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/75d7043853a42837e68111812f4d964b01e5101c", + "reference": "75d7043853a42837e68111812f4d964b01e5101c", "shasum": "" }, "require": { @@ -2670,7 +2661,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -2706,7 +2697,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.2" + "source": "https://github.com/symfony/http-client-contracts/tree/v3.6.0" }, "funding": [ { @@ -2722,7 +2713,7 @@ "type": "tidelift" } ], - "time": "2024-12-07T08:49:48+00:00" + "time": "2025-04-29T11:18:49+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -2883,16 +2874,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.5.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", "shasum": "" }, "require": { @@ -2910,7 +2901,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -2946,7 +2937,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" }, "funding": [ { @@ -2962,7 +2953,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2025-04-25T09:37:31+00:00" }, { "name": "tbachert/spi", @@ -3499,16 +3490,16 @@ }, { "name": "utopia-php/database", - "version": "0.69.5", + "version": "0.71.4", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "4abe53609dfc23b2ea82884d12b149df6a8af2f5" + "reference": "308cbeb65780f954f9f3abfff2ef17c5941ae00e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/4abe53609dfc23b2ea82884d12b149df6a8af2f5", - "reference": "4abe53609dfc23b2ea82884d12b149df6a8af2f5", + "url": "https://api.github.com/repos/utopia-php/database/zipball/308cbeb65780f954f9f3abfff2ef17c5941ae00e", + "reference": "308cbeb65780f954f9f3abfff2ef17c5941ae00e", "shasum": "" }, "require": { @@ -3549,9 +3540,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.69.5" + "source": "https://github.com/utopia-php/database/tree/0.71.4" }, - "time": "2025-05-17T08:01:51+00:00" + "time": "2025-06-10T15:47:50+00:00" }, { "name": "utopia-php/detector", @@ -3794,16 +3785,16 @@ }, { "name": "utopia-php/image", - "version": "0.8.3", + "version": "0.8.4", "source": { "type": "git", "url": "https://github.com/utopia-php/image.git", - "reference": "8820b0e53b3636b7bdf815e92394d333fef06f26" + "reference": "ce788ff0121a79286fdbe3ef3eba566de646df65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/image/zipball/8820b0e53b3636b7bdf815e92394d333fef06f26", - "reference": "8820b0e53b3636b7bdf815e92394d333fef06f26", + "url": "https://api.github.com/repos/utopia-php/image/zipball/ce788ff0121a79286fdbe3ef3eba566de646df65", + "reference": "ce788ff0121a79286fdbe3ef3eba566de646df65", "shasum": "" }, "require": { @@ -3837,9 +3828,9 @@ ], "support": { "issues": "https://github.com/utopia-php/image/issues", - "source": "https://github.com/utopia-php/image/tree/0.8.3" + "source": "https://github.com/utopia-php/image/tree/0.8.4" }, - "time": "2025-05-15T10:39:28+00:00" + "time": "2025-06-03T08:32:20+00:00" }, { "name": "utopia-php/locale", @@ -3948,16 +3939,16 @@ }, { "name": "utopia-php/messaging", - "version": "0.17.0", + "version": "0.18.0", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "c51915d0e030db3a3add37f1561751d18b2d9a85" + "reference": "c151aa5d4d475c788ca15c210b5b2017e21c41d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/c51915d0e030db3a3add37f1561751d18b2d9a85", - "reference": "c51915d0e030db3a3add37f1561751d18b2d9a85", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/c151aa5d4d475c788ca15c210b5b2017e21c41d6", + "reference": "c151aa5d4d475c788ca15c210b5b2017e21c41d6", "shasum": "" }, "require": { @@ -3993,22 +3984,22 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.17.0" + "source": "https://github.com/utopia-php/messaging/tree/0.18.0" }, - "time": "2025-05-12T16:14:08+00:00" + "time": "2025-05-15T05:00:03+00:00" }, { "name": "utopia-php/migration", - "version": "0.9.3", + "version": "0.10.1", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "e518d39eb550fde36bc5cf06c9bd7b2faf5dbedd" + "reference": "ea1c585df7ec5f346f061a11581fc9a91679966f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/e518d39eb550fde36bc5cf06c9bd7b2faf5dbedd", - "reference": "e518d39eb550fde36bc5cf06c9bd7b2faf5dbedd", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/ea1c585df7ec5f346f061a11581fc9a91679966f", + "reference": "ea1c585df7ec5f346f061a11581fc9a91679966f", "shasum": "" }, "require": { @@ -4049,9 +4040,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.9.3" + "source": "https://github.com/utopia-php/migration/tree/0.10.1" }, - "time": "2025-05-01T05:41:26+00:00" + "time": "2025-05-26T15:29:19+00:00" }, { "name": "utopia-php/orchestration", @@ -4105,16 +4096,16 @@ }, { "name": "utopia-php/platform", - "version": "0.7.6", + "version": "0.7.8", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "6bc7fbb43ec2b7f9ee5bdef5d4b5e4a81860950b" + "reference": "e3a4536c46f10988b1a446ec6b8dd8a9914be854" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/6bc7fbb43ec2b7f9ee5bdef5d4b5e4a81860950b", - "reference": "6bc7fbb43ec2b7f9ee5bdef5d4b5e4a81860950b", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/e3a4536c46f10988b1a446ec6b8dd8a9914be854", + "reference": "e3a4536c46f10988b1a446ec6b8dd8a9914be854", "shasum": "" }, "require": { @@ -4123,7 +4114,7 @@ "php": ">=8.0", "utopia-php/cli": "0.15.*", "utopia-php/framework": "0.33.*", - "utopia-php/queue": "0.10.*" + "utopia-php/queue": "0.11.*" }, "require-dev": { "laravel/pint": "1.*", @@ -4149,9 +4140,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.7.6" + "source": "https://github.com/utopia-php/platform/tree/0.7.8" }, - "time": "2025-05-18T20:31:24+00:00" + "time": "2025-05-30T10:05:43+00:00" }, { "name": "utopia-php/pools", @@ -4260,16 +4251,16 @@ }, { "name": "utopia-php/queue", - "version": "0.10.0", + "version": "0.11.1", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "0eccc559168ea72241c39a4c482d868314666be1" + "reference": "498bbbef418b1db71b51e1bb62f5d1d752ddd8d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/0eccc559168ea72241c39a4c482d868314666be1", - "reference": "0eccc559168ea72241c39a4c482d868314666be1", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/498bbbef418b1db71b51e1bb62f5d1d752ddd8d6", + "reference": "498bbbef418b1db71b51e1bb62f5d1d752ddd8d6", "shasum": "" }, "require": { @@ -4320,9 +4311,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.10.0" + "source": "https://github.com/utopia-php/queue/tree/0.11.1" }, - "time": "2025-04-17T12:15:52+00:00" + "time": "2025-05-30T11:50:34+00:00" }, { "name": "utopia-php/registry", @@ -4378,16 +4369,16 @@ }, { "name": "utopia-php/storage", - "version": "0.18.12", + "version": "0.18.13", "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", - "reference": "9a2556c39b5f4d9f8e79111fd34ec889b7bb1e97" + "reference": "3d8ce53ae042173bf230445e996056c5f65ded22" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/9a2556c39b5f4d9f8e79111fd34ec889b7bb1e97", - "reference": "9a2556c39b5f4d9f8e79111fd34ec889b7bb1e97", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/3d8ce53ae042173bf230445e996056c5f65ded22", + "reference": "3d8ce53ae042173bf230445e996056c5f65ded22", "shasum": "" }, "require": { @@ -4430,9 +4421,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.12" + "source": "https://github.com/utopia-php/storage/tree/0.18.13" }, - "time": "2025-05-15T07:55:58+00:00" + "time": "2025-05-26T13:10:35+00:00" }, { "name": "utopia-php/swoole", @@ -4593,16 +4584,16 @@ }, { "name": "utopia-php/vcs", - "version": "0.10.2", + "version": "0.10.5", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "1f9823ebcb8fd098607de0074f18f48e28985012" + "reference": "b358439dc387f6097019eb83ebb9fc258fe9da05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/1f9823ebcb8fd098607de0074f18f48e28985012", - "reference": "1f9823ebcb8fd098607de0074f18f48e28985012", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/b358439dc387f6097019eb83ebb9fc258fe9da05", + "reference": "b358439dc387f6097019eb83ebb9fc258fe9da05", "shasum": "" }, "require": { @@ -4636,9 +4627,9 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.10.2" + "source": "https://github.com/utopia-php/vcs/tree/0.10.5" }, - "time": "2025-04-17T04:35:25+00:00" + "time": "2025-06-10T15:01:16+00:00" }, { "name": "utopia-php/websocket", @@ -4816,16 +4807,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.40.17", + "version": "0.41.4", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "7e333c1003bfd4763e4d6f3a0a799fde5e7bc4de" + "reference": "07804269131f411576aac60c795a5ebc3afaa48a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/7e333c1003bfd4763e4d6f3a0a799fde5e7bc4de", - "reference": "7e333c1003bfd4763e4d6f3a0a799fde5e7bc4de", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/07804269131f411576aac60c795a5ebc3afaa48a", + "reference": "07804269131f411576aac60c795a5ebc3afaa48a", "shasum": "" }, "require": { @@ -4861,9 +4852,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.40.17" + "source": "https://github.com/appwrite/sdk-generator/tree/0.41.4" }, - "time": "2025-05-16T15:10:54+00:00" + "time": "2025-06-10T08:28:11+00:00" }, { "name": "doctrine/annotations", @@ -5340,16 +5331,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.4.0", + "version": "v5.5.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494" + "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", + "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", "shasum": "" }, "require": { @@ -5392,9 +5383,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" }, - "time": "2024-12-30T11:07:19+00:00" + "time": "2025-05-31T08:24:38+00:00" }, { "name": "phar-io/manifest", @@ -7262,23 +7253,24 @@ }, { "name": "symfony/console", - "version": "v7.2.6", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0e2e3f38c192e93e622e41ec37f4ca70cfedf218" + "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0e2e3f38c192e93e622e41ec37f4ca70cfedf218", - "reference": "0e2e3f38c192e93e622e41ec37f4ca70cfedf218", + "url": "https://api.github.com/repos/symfony/console/zipball/66c1440edf6f339fd82ed6c7caa76cb006211b44", + "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^6.4|^7.0" + "symfony/string": "^7.2" }, "conflict": { "symfony/dependency-injection": "<6.4", @@ -7335,7 +7327,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.2.6" + "source": "https://github.com/symfony/console/tree/v7.3.0" }, "funding": [ { @@ -7351,11 +7343,11 @@ "type": "tidelift" } ], - "time": "2025-04-07T19:09:28+00:00" + "time": "2025-05-24T10:34:04+00:00" }, { "name": "symfony/filesystem", - "version": "v7.2.0", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -7401,7 +7393,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.2.0" + "source": "https://github.com/symfony/filesystem/tree/v7.3.0" }, "funding": [ { @@ -7421,16 +7413,16 @@ }, { "name": "symfony/finder", - "version": "v7.2.2", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "87a71856f2f56e4100373e92529eed3171695cfb" + "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/87a71856f2f56e4100373e92529eed3171695cfb", - "reference": "87a71856f2f56e4100373e92529eed3171695cfb", + "url": "https://api.github.com/repos/symfony/finder/zipball/ec2344cf77a48253bbca6939aa3d2477773ea63d", + "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d", "shasum": "" }, "require": { @@ -7465,7 +7457,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.2.2" + "source": "https://github.com/symfony/finder/tree/v7.3.0" }, "funding": [ { @@ -7481,20 +7473,20 @@ "type": "tidelift" } ], - "time": "2024-12-30T19:00:17+00:00" + "time": "2024-12-30T19:00:26+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.2.0", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "7da8fbac9dcfef75ffc212235d76b2754ce0cf50" + "reference": "afb9a8038025e5dbc657378bfab9198d75f10fca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/7da8fbac9dcfef75ffc212235d76b2754ce0cf50", - "reference": "7da8fbac9dcfef75ffc212235d76b2754ce0cf50", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/afb9a8038025e5dbc657378bfab9198d75f10fca", + "reference": "afb9a8038025e5dbc657378bfab9198d75f10fca", "shasum": "" }, "require": { @@ -7532,7 +7524,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.2.0" + "source": "https://github.com/symfony/options-resolver/tree/v7.3.0" }, "funding": [ { @@ -7548,7 +7540,7 @@ "type": "tidelift" } ], - "time": "2024-11-20T11:17:29+00:00" + "time": "2025-04-04T13:12:05+00:00" }, { "name": "symfony/polyfill-ctype", @@ -7866,16 +7858,16 @@ }, { "name": "symfony/process", - "version": "v7.2.5", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "87b7c93e57df9d8e39a093d32587702380ff045d" + "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/87b7c93e57df9d8e39a093d32587702380ff045d", - "reference": "87b7c93e57df9d8e39a093d32587702380ff045d", + "url": "https://api.github.com/repos/symfony/process/zipball/40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", + "reference": "40c295f2deb408d5e9d2d32b8ba1dd61e36f05af", "shasum": "" }, "require": { @@ -7907,7 +7899,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.2.5" + "source": "https://github.com/symfony/process/tree/v7.3.0" }, "funding": [ { @@ -7923,20 +7915,20 @@ "type": "tidelift" } ], - "time": "2025-03-13T12:21:46+00:00" + "time": "2025-04-17T09:11:12+00:00" }, { "name": "symfony/string", - "version": "v7.2.6", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "a214fe7d62bd4df2a76447c67c6b26e1d5e74931" + "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/a214fe7d62bd4df2a76447c67c6b26e1d5e74931", - "reference": "a214fe7d62bd4df2a76447c67c6b26e1d5e74931", + "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125", + "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125", "shasum": "" }, "require": { @@ -7994,7 +7986,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.2.6" + "source": "https://github.com/symfony/string/tree/v7.3.0" }, "funding": [ { @@ -8010,7 +8002,7 @@ "type": "tidelift" } ], - "time": "2025-04-20T20:18:16+00:00" + "time": "2025-04-20T20:19:01+00:00" }, { "name": "textalk/websocket", @@ -8242,7 +8234,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -8266,5 +8258,5 @@ "platform-overrides": { "php": "8.3" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/docker-compose.yml b/docker-compose.yml index 7888bebeea..b2e059e5fc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -214,7 +214,7 @@ services: appwrite-console: <<: *x-logging container_name: appwrite-console - image: appwrite/console:6.0.7 + image: appwrite/console:6.0.32 restart: unless-stopped networks: - appwrite @@ -969,7 +969,7 @@ services: hostname: exc1 <<: *x-logging stop_signal: SIGINT - image: openruntimes/executor:0.7.16 + image: openruntimes/executor:0.7.20 restart: unless-stopped networks: - appwrite @@ -1057,7 +1057,7 @@ services: command: "mysqld --innodb-flush-method=fsync" postgresql: - image: postgres:15 + image: postgres:17.5 container_name: appwrite-postgresql <<: *x-logging networks: diff --git a/docs/sdks/flutter/CHANGELOG.md b/docs/sdks/flutter/CHANGELOG.md index 674742b845..f707c645e6 100644 --- a/docs/sdks/flutter/CHANGELOG.md +++ b/docs/sdks/flutter/CHANGELOG.md @@ -1,5 +1,15 @@ # Change Log +## 16.1.1 + +* Update `flutter_web_auth_2` dependency to version 4.1.0 +* Update `auth.html` example in README.md to align with `flutter_web_auth_2` documentation + +## 16.1.0 + +* Add `setDevKey` method to Client service +* Add `upsertDocument` method to Databases service + ## 16.0.0 * Remove `Gif` from ImageFormat enum diff --git a/docs/sdks/flutter/GETTING_STARTED.md b/docs/sdks/flutter/GETTING_STARTED.md index 8d239402b3..c3a1a8d33d 100644 --- a/docs/sdks/flutter/GETTING_STARTED.md +++ b/docs/sdks/flutter/GETTING_STARTED.md @@ -56,13 +56,25 @@ For web in order to capture the OAuth2 callback URL and send it to the applicati ```html Authentication complete -

Authentication is complete. If this does not happen automatically, please -close the window. +

Authentication is complete. If this does not happen automatically, please close the window.

``` diff --git a/src/Appwrite/Deletes/Targets.php b/src/Appwrite/Deletes/Targets.php index 95e744ddf1..794ab0b87a 100644 --- a/src/Appwrite/Deletes/Targets.php +++ b/src/Appwrite/Deletes/Targets.php @@ -27,7 +27,7 @@ class Targets $database->deleteDocuments( 'subscribers', [ - Query::equal('targetInternalId', [$target->getInternalId()]), + Query::equal('targetInternalId', [$target->getSequence()]), Query::orderAsc(), ], Database::DELETE_BATCH_SIZE, @@ -35,7 +35,7 @@ class Targets $topicId = $subscriber->getAttribute('topicId'); $topicInternalId = $subscriber->getAttribute('topicInternalId'); $topic = $database->getDocument('topics', $topicId); - if (!$topic->isEmpty() && $topic->getInternalId() === $topicInternalId) { + if (!$topic->isEmpty() && $topic->getSequence() === $topicInternalId) { $totalAttribute = match ($target->getAttribute('providerType')) { MESSAGE_TYPE_EMAIL => 'emailTotal', MESSAGE_TYPE_SMS => 'smsTotal', diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index d699a45417..934647f7c3 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -286,13 +286,6 @@ class Event return $this; } - public function setParamSensitive(string $key): self - { - $this->sensitive[$key] = true; - - return $this; - } - /** * Get param of event. * @@ -327,7 +320,7 @@ class Event if ($this->project) { $trimmed['project'] = new Document([ '$id' => $this->project->getId(), - '$internalId' => $this->project->getInternalId(), + '$sequence' => $this->project->getSequence(), 'database' => $this->project->getAttribute('database') ]); } diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 296434ed57..3af6d9962c 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -124,6 +124,8 @@ class Exception extends \Exception /** Membership */ public const MEMBERSHIP_NOT_FOUND = 'membership_not_found'; public const MEMBERSHIP_ALREADY_CONFIRMED = 'membership_already_confirmed'; + public const MEMBERSHIP_DELETION_PROHIBITED = 'membership_deletion_prohibited'; + public const MEMBERSHIP_DOWNGRADE_PROHIBITED = 'membership_downgrade_prohibited'; /** Avatars */ public const AVATAR_SET_NOT_FOUND = 'avatar_set_not_found'; diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index 568132ceb1..d122841d68 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -2,14 +2,14 @@ namespace Appwrite\Messaging\Adapter; -use Appwrite\Messaging\Adapter; +use Appwrite\Messaging\Adapter as MessagingAdapter; +use Appwrite\PubSub\Adapter\Pool as PubSubPool; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; -use Utopia\Pools\Pool; -class Realtime extends Adapter +class Realtime extends MessagingAdapter { /** * Connection Tree @@ -36,12 +36,12 @@ class Realtime extends Adapter */ public array $subscriptions = []; - private Pool $pubsubPool; + private PubSubPool $pubSubPool; public function __construct() { global $register; - $this->pubsubPool = $register->get('pools')->get('pubsub'); + $this->pubSubPool = new PubSubPool($register->get('pools')->get('pubsub')); } /** @@ -132,11 +132,12 @@ class Realtime extends Adapter * Sends an event to the Realtime Server * @param string $projectId * @param array $payload - * @param string $event + * @param array $events * @param array $channels * @param array $roles * @param array $options * @return void + * @throws \Exception */ public function send(string $projectId, array $payload, array $events, array $channels, array $roles, array $options = []): void { @@ -147,7 +148,7 @@ class Realtime extends Adapter $permissionsChanged = array_key_exists('permissionsChanged', $options) && $options['permissionsChanged']; $userId = array_key_exists('userId', $options) ? $options['userId'] : null; - $message = [ + $this->pubSubPool->publish('realtime', json_encode([ 'project' => $projectId, 'roles' => $roles, 'permissionsChanged' => $permissionsChanged, @@ -158,9 +159,7 @@ class Realtime extends Adapter 'timestamp' => DateTime::formatTz(DateTime::now()), 'payload' => $payload ] - ]; - - $this->pubsubPool->use(fn (\Appwrite\PubSub\Adapter $pubsub) => $pubsub->publish('realtime', json_encode($message))); + ])); } /** @@ -175,8 +174,9 @@ class Realtime extends Adapter * - 1,121.328 ms (±0.84%) | 1,000,000 Connections / 10,000,000 Subscriptions * * @param array $event + * @return int[]|string[] */ - public function getSubscribers(array $event) + public function getSubscribers(array $event): array { $receivers = []; @@ -230,7 +230,7 @@ class Realtime extends Adapter foreach ($channels as $key => $value) { switch (true) { - case strpos($key, 'account.') === 0: + case \str_starts_with($key, 'account.'): unset($channels[$key]); break; @@ -353,7 +353,6 @@ class Realtime extends Adapter } break; - case 'sites': if ($parts[2] === 'deployments') { $channels[] = 'console'; @@ -361,7 +360,6 @@ class Realtime extends Adapter $projectId = 'console'; $roles = [Role::team($project->getAttribute('teamId'))->toString()]; } - break; case 'migrations': $channels[] = 'console'; diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 0b1e462d8e..9071e0cabe 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -85,6 +85,10 @@ abstract class Migration '1.6.2' => 'V21', '1.7.0-RC1' => 'V22', '1.7.0' => 'V22', + '1.7.1' => 'V22', + '1.7.2' => 'V22', + '1.7.3' => 'V22', + '1.7.4' => 'V22', ]; /** @@ -153,7 +157,7 @@ abstract class Migration */ public function forEachDocument(callable $callback): void { - $projectInternalId = $this->project->getInternalId(); + $projectInternalId = $this->project->getSequence(); $collections = match ($projectInternalId) { 'console' => $this->collections['console'], @@ -206,7 +210,7 @@ abstract class Migration { $name ??= $id; - $collectionType = match ($this->project->getInternalId()) { + $collectionType = match ($this->project->getSequence()) { 'console' => 'console', default => 'projects', }; @@ -257,7 +261,7 @@ abstract class Migration ): void { $from ??= $collectionId; - $collectionType = match ($this->project->getInternalId()) { + $collectionType = match ($this->project->getSequence()) { 'console' => 'console', default => 'projects', }; @@ -322,7 +326,7 @@ abstract class Migration ): void { $from ??= $collectionId; - $collectionType = match ($this->project->getInternalId()) { + $collectionType = match ($this->project->getSequence()) { 'console' => 'console', default => 'projects', }; @@ -380,7 +384,7 @@ abstract class Migration { $from ??= $collectionId; - $collectionType = match ($this->project->getInternalId()) { + $collectionType = match ($this->project->getSequence()) { 'console' => 'console', default => 'projects', }; @@ -426,7 +430,7 @@ abstract class Migration */ protected function changeAttributeInternalType(string $collection, string $attribute, string $type): void { - $stmt = $this->pdo->prepare("ALTER TABLE `{$this->dbForProject->getDatabase()}`.`_{$this->project->getInternalId()}_{$collection}` MODIFY `$attribute` $type;"); + $stmt = $this->pdo->prepare("ALTER TABLE `{$this->dbForProject->getDatabase()}`.`_{$this->project->getSequence()}_{$collection}` MODIFY `$attribute` $type;"); try { $stmt->execute(); diff --git a/src/Appwrite/Migration/Version/V15.php b/src/Appwrite/Migration/Version/V15.php index 8eab916f19..15331e9a37 100644 --- a/src/Appwrite/Migration/Version/V15.php +++ b/src/Appwrite/Migration/Version/V15.php @@ -95,7 +95,7 @@ class V15 extends Migration $this->migrateStatsMetric('storage.files.delete', 'files.$all.requests.delete'); foreach ($this->documentsIterator('buckets') as $bucket) { - $bucketTable = "bucket_{$bucket->getInternalId()}"; + $bucketTable = "bucket_{$bucket->getSequence()}"; $this->createPermissionsColumn($bucketTable); $this->migrateDateTimeAttribute($bucketTable, '_createdAt'); @@ -178,7 +178,7 @@ class V15 extends Migration * Migrate every Database. */ foreach ($this->documentsIterator('databases') as $database) { - $databaseTable = "database_{$database->getInternalId()}"; + $databaseTable = "database_{$database->getSequence()}"; $this->createPermissionsColumn($databaseTable); $this->migrateDateTimeAttribute($databaseTable, '_createdAt'); $this->migrateDateTimeAttribute($databaseTable, '_updatedAt'); @@ -216,7 +216,7 @@ class V15 extends Migration */ Console::info("Migrating Collections of {$database->getId()} ({$database->getAttribute('name')})"); foreach ($this->documentsIterator($databaseTable) as $collection) { - $collectionTable = "{$databaseTable}_collection_{$collection->getInternalId()}"; + $collectionTable = "{$databaseTable}_collection_{$collection->getSequence()}"; $this->createPermissionsColumn($collectionTable); $this->migrateDateTimeAttribute($collectionTable, '_createdAt'); $this->migrateDateTimeAttribute($collectionTable, '_updatedAt'); @@ -277,7 +277,7 @@ class V15 extends Migration $this->removeWritePermissions($databaseTable); try { - $this->dbForProject->deleteAttribute("database_{$database->getInternalId()}", 'permission'); + $this->dbForProject->deleteAttribute("database_{$database->getSequence()}", 'permission'); } catch (\Throwable $th) { Console::warning("'permission' from {$databaseTable}: {$th->getMessage()}"); } @@ -293,7 +293,7 @@ class V15 extends Migration protected function removeWritePermissions(string $table): void { try { - $this->pdo->prepare("DELETE FROM `{$this->dbForProject->getDatabase()}`.`_{$this->project->getInternalId()}_{$table}_perms` WHERE _type = 'write'")->execute(); + $this->pdo->prepare("DELETE FROM `{$this->dbForProject->getDatabase()}`.`_{$this->project->getSequence()}_{$table}_perms` WHERE _type = 'write'")->execute(); } catch (\Throwable $th) { Console::warning("Remove 'write' permissions from {$table}: {$th->getMessage()}"); } @@ -309,7 +309,7 @@ class V15 extends Migration */ protected function getSQLColumnTypes(string $table): array { - $query = $this->pdo->prepare("SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '_{$this->project->getInternalId()}_{$table}' AND table_schema = '{$this->dbForProject->getDatabase()}'"); + $query = $this->pdo->prepare("SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '_{$this->project->getSequence()}_{$table}' AND table_schema = '{$this->dbForProject->getDatabase()}'"); $query->execute(); return array_reduce($query->fetchAll(), function (array $carry, array $item) { @@ -331,8 +331,8 @@ class V15 extends Migration if ($columns[$attribute] === 'int') { try { - $this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->dbForProject->getDatabase()}`.`_{$this->project->getInternalId()}_{$table}` MODIFY {$attribute} VARCHAR(64)")->execute(); - $this->pdo->prepare("UPDATE `{$this->dbForProject->getDatabase()}`.`_{$this->project->getInternalId()}_{$table}` SET {$attribute} = IF({$attribute} = 0, NULL, FROM_UNIXTIME({$attribute}))")->execute(); + $this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->dbForProject->getDatabase()}`.`_{$this->project->getSequence()}_{$table}` MODIFY {$attribute} VARCHAR(64)")->execute(); + $this->pdo->prepare("UPDATE `{$this->dbForProject->getDatabase()}`.`_{$this->project->getSequence()}_{$table}` SET {$attribute} = IF({$attribute} = 0, NULL, FROM_UNIXTIME({$attribute}))")->execute(); $columns[$attribute] = 'varchar'; } catch (\Throwable $th) { Console::warning($th->getMessage()); @@ -341,7 +341,7 @@ class V15 extends Migration if ($columns[$attribute] === 'varchar') { try { - $this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->dbForProject->getDatabase()}`.`_{$this->project->getInternalId()}_{$table}` MODIFY {$attribute} DATETIME(3)")->execute(); + $this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->dbForProject->getDatabase()}`.`_{$this->project->getSequence()}_{$table}` MODIFY {$attribute} DATETIME(3)")->execute(); } catch (\Throwable $th) { Console::warning($th->getMessage()); } @@ -387,7 +387,7 @@ class V15 extends Migration if (!array_key_exists('_permissions', $columns)) { try { - $this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->dbForProject->getDatabase()}`.`_{$this->project->getInternalId()}_{$table}` ADD `_permissions` MEDIUMTEXT DEFAULT NULL")->execute(); + $this->pdo->prepare("ALTER TABLE IF EXISTS `{$this->dbForProject->getDatabase()}`.`_{$this->project->getSequence()}_{$table}` ADD `_permissions` MEDIUMTEXT DEFAULT NULL")->execute(); } catch (\Throwable $th) { Console::warning("Add '_permissions' column to '{$table}': {$th->getMessage()}"); } @@ -408,7 +408,7 @@ class V15 extends Migration { $table ??= $document->getCollection(); - $query = $this->pdo->prepare("SELECT * FROM `{$this->dbForProject->getDatabase()}`.`_{$this->project->getInternalId()}_{$table}_perms` WHERE _document = '{$document->getId()}'"); + $query = $this->pdo->prepare("SELECT * FROM `{$this->dbForProject->getDatabase()}`.`_{$this->project->getSequence()}_{$table}_perms` WHERE _document = '{$document->getId()}'"); $query->execute(); $results = $query->fetchAll(); $permissions = []; @@ -466,7 +466,7 @@ class V15 extends Migration Console::log("Migrating Collection \"{$id}\""); - $this->dbForProject->setNamespace("_{$this->project->getInternalId()}"); + $this->dbForProject->setNamespace("_{$this->project->getSequence()}"); switch ($id) { case '_metadata': @@ -746,7 +746,7 @@ class V15 extends Migration Permission::delete(Role::any()), ], 'functionId' => $function->getId(), - 'functionInternalId' => $function->getInternalId(), + 'functionInternalId' => $function->getSequence(), 'key' => (string) $key, 'value' => (string) $value, 'search' => implode(' ', [$variableId, $key, $function->getId()]) @@ -1470,9 +1470,9 @@ class V15 extends Migration $from = $this->pdo->quote($from); $to = $this->pdo->quote($to); - $this->pdo->prepare("UPDATE `{$this->dbForProject->getDatabase()}`.`_{$this->project->getInternalId()}_stats` SET metric = {$to} WHERE metric = {$from}")->execute(); + $this->pdo->prepare("UPDATE `{$this->dbForProject->getDatabase()}`.`_{$this->project->getSequence()}_stats` SET metric = {$to} WHERE metric = {$from}")->execute(); } catch (\Throwable $th) { - Console::warning("Migrating steps from {$this->dbForProject->getDatabase()}`.`_{$this->project->getInternalId()}_stats:" . $th->getMessage()); + Console::warning("Migrating steps from {$this->dbForProject->getDatabase()}`.`_{$this->project->getSequence()}_stats:" . $th->getMessage()); } } diff --git a/src/Appwrite/Migration/Version/V16.php b/src/Appwrite/Migration/Version/V16.php index 34407a0471..9d72af9563 100644 --- a/src/Appwrite/Migration/Version/V16.php +++ b/src/Appwrite/Migration/Version/V16.php @@ -45,7 +45,7 @@ class V16 extends Migration Console::log("Migrating Collection \"{$id}\""); - $this->dbForProject->setNamespace("_{$this->project->getInternalId()}"); + $this->dbForProject->setNamespace("_{$this->project->getSequence()}"); switch ($id) { case 'sessions': diff --git a/src/Appwrite/Migration/Version/V17.php b/src/Appwrite/Migration/Version/V17.php index f7cb08d6f4..fbbd4bfde0 100644 --- a/src/Appwrite/Migration/Version/V17.php +++ b/src/Appwrite/Migration/Version/V17.php @@ -44,7 +44,7 @@ class V17 extends Migration protected function migrateBuckets(): void { foreach ($this->documentsIterator('buckets') as $bucket) { - $id = "bucket_{$bucket->getInternalId()}"; + $id = "bucket_{$bucket->getSequence()}"; try { $this->dbForProject->updateAttribute($id, 'mimeType', Database::VAR_STRING, 255, true, false); @@ -67,7 +67,7 @@ class V17 extends Migration Console::log("Migrating Collection \"{$id}\""); - $this->dbForProject->setNamespace("_{$this->project->getInternalId()}"); + $this->dbForProject->setNamespace("_{$this->project->getSequence()}"); switch ($id) { case 'builds': diff --git a/src/Appwrite/Migration/Version/V18.php b/src/Appwrite/Migration/Version/V18.php index a5fae3789b..aa2ad35cca 100644 --- a/src/Appwrite/Migration/Version/V18.php +++ b/src/Appwrite/Migration/Version/V18.php @@ -26,7 +26,7 @@ class V18 extends Migration } Console::log('Migrating Project: ' . $this->project->getAttribute('name') . ' (' . $this->project->getId() . ')'); - $this->dbForProject->setNamespace("_{$this->project->getInternalId()}"); + $this->dbForProject->setNamespace("_{$this->project->getSequence()}"); $this->addDocumentSecurityToProject(); Console::info('Migrating Databases'); @@ -48,12 +48,12 @@ class V18 extends Migration private function migrateDatabases(): void { foreach ($this->documentsIterator('databases') as $database) { - $databaseTable = "database_{$database->getInternalId()}"; + $databaseTable = "database_{$database->getSequence()}"; Console::info("Migrating Collections of {$database->getId()} ({$database->getAttribute('name')})"); foreach ($this->documentsIterator($databaseTable) as $collection) { - $collectionTable = "{$databaseTable}_collection_{$collection->getInternalId()}"; + $collectionTable = "{$databaseTable}_collection_{$collection->getSequence()}"; foreach ($collection['attributes'] ?? [] as $attribute) { if ($attribute['type'] !== Database::VAR_FLOAT) { @@ -197,7 +197,7 @@ class V18 extends Migration * Set the bucket permission in the metadata table */ try { - $internalBucketId = "bucket_{$this->project->getInternalId()}"; + $internalBucketId = "bucket_{$this->project->getSequence()}"; $permissions = $document->getPermissions(); $fileSecurity = $document->getAttribute('fileSecurity', false); $this->dbForProject->updateCollection($internalBucketId, $permissions, $fileSecurity); @@ -224,8 +224,8 @@ class V18 extends Migration // Nonetheless, there's nothing else we can do here. break; } - $internalId = $user->getInternalId(); - $document->setAttribute('userId', $internalId); + $sequence = $user->getSequence(); + $document->setAttribute('userId', $sequence); $data = $document->getAttribute('data', []); $data['userId'] = $user->getId(); $document->setAttribute('data', $data); @@ -244,7 +244,7 @@ class V18 extends Migration /** * Create 'documentSecurity' column */ - $this->pdo->prepare("ALTER TABLE `{$this->dbForProject->getDatabase()}`.`_{$this->project->getInternalId()}__metadata` ADD COLUMN IF NOT EXISTS documentSecurity TINYINT(1);")->execute(); + $this->pdo->prepare("ALTER TABLE `{$this->dbForProject->getDatabase()}`.`_{$this->project->getSequence()}__metadata` ADD COLUMN IF NOT EXISTS documentSecurity TINYINT(1);")->execute(); } catch (\Throwable $th) { Console::warning($th->getMessage()); } @@ -253,7 +253,7 @@ class V18 extends Migration /** * Set 'documentSecurity' column to 1 if NULL */ - $this->pdo->prepare("UPDATE `{$this->dbForProject->getDatabase()}`.`_{$this->project->getInternalId()}__metadata` SET documentSecurity = 1 WHERE documentSecurity IS NULL")->execute(); + $this->pdo->prepare("UPDATE `{$this->dbForProject->getDatabase()}`.`_{$this->project->getSequence()}__metadata` SET documentSecurity = 1 WHERE documentSecurity IS NULL")->execute(); } catch (\Throwable $th) { Console::warning($th->getMessage()); } diff --git a/src/Appwrite/Migration/Version/V19.php b/src/Appwrite/Migration/Version/V19.php index cae27cc6ed..d4dda02d75 100644 --- a/src/Appwrite/Migration/Version/V19.php +++ b/src/Appwrite/Migration/Version/V19.php @@ -28,7 +28,7 @@ class V19 extends Migration } Console::log('Migrating Project: ' . $this->project->getAttribute('name') . ' (' . $this->project->getId() . ')'); - $this->dbForProject->setNamespace("_{$this->project->getInternalId()}"); + $this->dbForProject->setNamespace("_{$this->project->getSequence()}"); Console::info('Migrating Collections'); $this->migrateCollections(); @@ -100,7 +100,7 @@ class V19 extends Migration protected function migrateBuckets(): void { foreach ($this->documentsIterator('buckets') as $bucket) { - $id = "bucket_{$bucket->getInternalId()}"; + $id = "bucket_{$bucket->getSequence()}"; Console::log("Migrating Bucket {$id} {$bucket->getId()} ({$bucket->getAttribute('name')})"); try { @@ -121,7 +121,7 @@ class V19 extends Migration */ private function migrateCollections(): void { - $internalProjectId = $this->project->getInternalId(); + $internalProjectId = $this->project->getSequence(); $collectionType = match ($internalProjectId) { 'console' => 'console', default => 'projects', @@ -680,7 +680,7 @@ class V19 extends Migration case 'builds': $deploymentId = $document->getAttribute('deploymentId'); $deployment = $this->dbForProject->getDocument('deployments', $deploymentId); - $document->setAttribute('deploymentInternalId', $deployment->getInternalId()); + $document->setAttribute('deploymentInternalId', $deployment->getSequence()); $stdout = $document->getAttribute('stdout', ''); $stderr = $document->getAttribute('stderr', ''); @@ -692,12 +692,12 @@ class V19 extends Migration case 'deployments': $resourceId = $document->getAttribute('resourceId'); $function = $this->dbForProject->getDocument('functions', $resourceId); - $document->setAttribute('resourceInternalId', $function->getInternalId()); + $document->setAttribute('resourceInternalId', $function->getSequence()); $buildId = $document->getAttribute('buildId'); if (!empty($buildId)) { $build = $this->dbForProject->getDocument('builds', $buildId); - $document->setAttribute('buildInternalId', $build->getInternalId()); + $document->setAttribute('buildInternalId', $build->getSequence()); } $commands = $this->getFunctionCommands($function); @@ -707,11 +707,11 @@ class V19 extends Migration case 'executions': $functionId = $document->getAttribute('functionId'); $function = $this->dbForProject->getDocument('functions', $functionId); - $document->setAttribute('functionInternalId', $function->getInternalId()); + $document->setAttribute('functionInternalId', $function->getSequence()); $deploymentId = $document->getAttribute('deploymentId'); $deployment = $this->dbForProject->getDocument('deployments', $deploymentId); - $document->setAttribute('deploymentInternalId', $deployment->getInternalId()); + $document->setAttribute('deploymentInternalId', $deployment->getSequence()); break; case 'functions': $document->setAttribute('live', $document->getAttribute('live', true)); @@ -721,7 +721,7 @@ class V19 extends Migration if (!empty($deploymentId)) { $deployment = $this->dbForProject->getDocument('deployments', $deploymentId); - $document->setAttribute('deploymentInternalId', $deployment->getInternalId()); + $document->setAttribute('deploymentInternalId', $deployment->getSequence()); $document->setAttribute('entrypoint', $deployment->getAttribute('entrypoint')); } @@ -733,7 +733,7 @@ class V19 extends Migration 'region' => $project->getAttribute('region'), 'resourceType' => 'function', 'resourceId' => $document->getId(), - 'resourceInternalId' => $document->getInternalId(), + 'resourceInternalId' => $document->getSequence(), 'resourceUpdatedAt' => DateTime::now(), 'projectId' => $this->project->getId(), 'schedule' => $document->getAttribute('schedule'), @@ -741,7 +741,7 @@ class V19 extends Migration ])); $document->setAttribute('scheduleId', $schedule->getId()); - $document->setAttribute('scheduleInternalId', $schedule->getInternalId()); + $document->setAttribute('scheduleInternalId', $schedule->getSequence()); } break; @@ -799,7 +799,7 @@ class V19 extends Migration */ public function forEachDocument(callable $callback): void { - $internalProjectId = $this->project->getInternalId(); + $internalProjectId = $this->project->getSequence(); $collections = match ($internalProjectId) { 'console' => $this->collections['console'], diff --git a/src/Appwrite/Migration/Version/V20.php b/src/Appwrite/Migration/Version/V20.php index 3a9bba6772..9ff041eb33 100644 --- a/src/Appwrite/Migration/Version/V20.php +++ b/src/Appwrite/Migration/Version/V20.php @@ -36,13 +36,13 @@ class V20 extends Migration } Console::log('Migrating Project: ' . $this->project->getAttribute('name') . ' (' . $this->project->getId() . ')'); - $this->dbForProject->setNamespace("_{$this->project->getInternalId()}"); + $this->dbForProject->setNamespace("_{$this->project->getSequence()}"); Console::info('Migrating Collections'); $this->migrateCollections(); // No need to migrate stats for console - if ($this->project->getInternalId() !== 'console') { + if ($this->project->getSequence() !== 'console') { $this->migrateUsageMetrics('project.$all.network.requests', 'network.requests'); $this->migrateUsageMetrics('project.$all.network.outbound', 'network.outbound'); $this->migrateUsageMetrics('project.$all.network.inbound', 'network.inbound'); @@ -71,7 +71,7 @@ class V20 extends Migration */ private function migrateCollections(): void { - $internalProjectId = $this->project->getInternalId(); + $internalProjectId = $this->project->getSequence(); $collectionType = match ($internalProjectId) { 'console' => 'console', default => 'projects', @@ -510,7 +510,7 @@ class V20 extends Migration Console::log("Migrating Functions usage stats of {$function->getId()} ({$function->getAttribute('name')})"); $functionId = $function->getId(); - $functionInternalId = $function->getInternalId(); + $functionInternalId = $function->getSequence(); $this->migrateUsageMetrics("deployment.$functionId.storage.size", "function.$functionInternalId.deployments.storage"); $this->migrateUsageMetrics("builds.$functionId.compute.total", "$functionInternalId.builds"); @@ -536,22 +536,22 @@ class V20 extends Migration foreach ($this->documentsIterator('databases') as $database) { Console::log("Migrating Collections of {$database->getId()} ({$database->getAttribute('name')})"); - $databaseTable = "database_{$database->getInternalId()}"; + $databaseTable = "database_{$database->getSequence()}"; // Database level $databaseId = $database->getId(); - $databaseInternalId = $database->getInternalId(); + $databaseInternalId = $database->getSequence(); $this->migrateUsageMetrics("collections.$databaseId.count.total", "$databaseInternalId.collections"); $this->migrateUsageMetrics("documents.$databaseId.count.total", "$databaseInternalId.documents"); foreach ($this->documentsIterator($databaseTable) as $collection) { - $collectionTable = "{$databaseTable}_collection_{$collection->getInternalId()}"; + $collectionTable = "{$databaseTable}_collection_{$collection->getSequence()}"; Console::log("Migrating Collections of {$collectionTable} {$collection->getId()} ({$collection->getAttribute('name')})"); // Collection level $collectionId = $collection->getId(); - $collectionInternalId = $collection->getInternalId(); + $collectionInternalId = $collection->getSequence(); $this->migrateUsageMetrics("documents.$databaseId/$collectionId.count.total", "$databaseInternalId.$collectionInternalId.documents"); } @@ -573,12 +573,12 @@ class V20 extends Migration $this->migrateUsageMetrics('files.$all.storage.size', 'files.storage'); foreach ($this->documentsIterator('buckets') as $bucket) { - $id = "bucket_{$bucket->getInternalId()}"; + $id = "bucket_{$bucket->getSequence()}"; Console::log("Migrating Bucket {$id} {$bucket->getId()} ({$bucket->getAttribute('name')})"); // Bucket level $bucketId = $bucket->getId(); - $bucketInternalId = $bucket->getInternalId(); + $bucketInternalId = $bucket->getSequence(); $this->migrateUsageMetrics("files.$bucketId.count.total", "$bucketInternalId.files"); $this->migrateUsageMetrics("files.$bucketId.storage.size", "$bucketInternalId.files.storage"); @@ -605,7 +605,7 @@ class V20 extends Migration $target = new Document([ '$id' => ID::unique(), 'userId' => $document->getId(), - 'userInternalId' => $document->getInternalId(), + 'userInternalId' => $document->getSequence(), 'providerType' => MESSAGE_TYPE_EMAIL, 'identifier' => $document->getAttribute('email'), ]); @@ -620,7 +620,7 @@ class V20 extends Migration $target = new Document([ '$id' => ID::unique(), 'userId' => $document->getId(), - 'userInternalId' => $document->getInternalId(), + 'userInternalId' => $document->getSequence(), 'providerType' => MESSAGE_TYPE_SMS, 'identifier' => $document->getAttribute('phone'), ]); diff --git a/src/Appwrite/Migration/Version/V21.php b/src/Appwrite/Migration/Version/V21.php index 38e8a8d513..891d2a92fe 100644 --- a/src/Appwrite/Migration/Version/V21.php +++ b/src/Appwrite/Migration/Version/V21.php @@ -29,12 +29,12 @@ class V21 extends Migration } Console::log('Migrating Project: ' . $this->project->getAttribute('name') . ' (' . $this->project->getId() . ')'); - $this->dbForProject->setNamespace("_{$this->project->getInternalId()}"); + $this->dbForProject->setNamespace("_{$this->project->getSequence()}"); Console::info('Migrating Collections'); $this->migrateCollections(); - if ($this->project->getInternalId() !== 'console') { + if ($this->project->getSequence() !== 'console') { Console::info('Migrating Buckets'); $this->migrateBuckets(); } @@ -51,7 +51,7 @@ class V21 extends Migration */ private function migrateCollections(): void { - $internalProjectId = $this->project->getInternalId(); + $internalProjectId = $this->project->getSequence(); $collectionType = match ($internalProjectId) { 'console' => 'console', default => 'projects', @@ -251,7 +251,7 @@ class V21 extends Migration private function migrateBuckets(): void { $this->dbForProject->forEach('buckets', function (Document $bucket) { - $bucketId = 'bucket_' . $bucket['$internalId']; + $bucketId = 'bucket_' . $bucket['$sequence']; Console::log("Migrating Bucket {$bucketId} {$bucket->getId()} ({$bucket->getAttribute('name')})"); diff --git a/src/Appwrite/Migration/Version/V22.php b/src/Appwrite/Migration/Version/V22.php index e485fb5bd0..ba9c9dc126 100644 --- a/src/Appwrite/Migration/Version/V22.php +++ b/src/Appwrite/Migration/Version/V22.php @@ -12,6 +12,7 @@ use Utopia\Database\Exception\Conflict; use Utopia\Database\Exception\Structure; use Utopia\Database\Exception\Timeout; use Utopia\Database\Query; +use Utopia\System\System; class V22 extends Migration { @@ -49,7 +50,7 @@ class V22 extends Migration */ private function migrateCollections(): void { - $projectInternalId = $this->project->getInternalId(); + $projectInternalId = $this->project->getSequence(); if (empty($projectInternalId)) { throw new Exception('Project ID is null'); @@ -149,9 +150,9 @@ class V22 extends Migration ]; foreach ($indexes as $index) { try { - $this->createIndexFromCollection($this->dbForProject, $id, $index); + $this->dbForProject->deleteIndex($id, $index); } catch (Throwable $th) { - Console::warning("Failed to create index \"$index\" from {$id}: {$th->getMessage()}"); + Console::warning("Failed to delete index \"$index\" from {$id}: {$th->getMessage()}"); } } $this->dbForProject->purgeCachedCollection($id); @@ -324,7 +325,9 @@ class V22 extends Migration 4. Fill "trigger" with "manual" 5. Fill "deploymentResourceType". If "resourceType" is "function", set "deploymentResourceType" to "function" 6. Fill "search" with "{$id} {domain}" - 7. Fill "deploymentId" and "deploymentInternalId". If "deploymentResourceType" is "function", get project DB, and find function with ID "resourceId". Then fill rule's "deploymentId" with function's "deployment", and "deploymentId" as backup + 7. Set "region" to project region + 8. Fill "owner" with "Appwrite" if "domain" ends with "functions" or "sites" + 9. Fill "deploymentId" and "deploymentInternalId". If "deploymentResourceType" is "function", get project DB, and find function with ID "resourceId". Then fill rule's "deploymentId" with function's "deployment", and "deploymentId" as backup */ $deploymentResourceType = null; @@ -346,11 +349,38 @@ class V22 extends Migration ->setAttribute('deploymentResourceType', $document->getAttribute('deploymentResourceType', $deploymentResourceType)) ->setAttribute('search', \implode(' ', [$document->getId(), $document->getAttribute('domain', '')])); + $project = $this->dbForProject->getDocument('projects', $document->getAttribute('projectId')); + + if ($project->isEmpty()) { + Console::warning("Project \"{$document->getAttribute('projectId')}\" not found for rule \"{$document->getId()}\""); + $document->setAttribute('region', System::getEnv('_APP_REGION', 'default')); + break; + } + + $document->setAttribute('region', $project->getAttribute('region', System::getEnv('_APP_REGION', 'default'))); + + $domain = $document->getAttribute('domain', ''); + $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); + $owner = $document->getAttribute('owner', ''); + if ( + empty($owner) && + (!empty($functionsDomain) && \str_ends_with($domain, $functionsDomain)) || + (!empty($sitesDomain) && \str_ends_with($domain, $sitesDomain)) + ) { + $document->setAttribute('owner', 'Appwrite'); + } + if ($deploymentResourceType === 'function') { - $project = $this->dbForProject->getDocument('projects', $document->getAttribute('projectId')); $dbForOwnerProject = ($this->getProjectDB)($project); $function = $dbForOwnerProject->getDocument('functions', $resourceId); - $deploymentId = $function->getAttribute('deployment', $function->getAttribute('deploymentId', $document->getAttribute('deploymentId'))); + + if ($function->isEmpty()) { + Console::warning("Function \"{$resourceId}\" not found for rule \"{$document->getId()}\""); + break; + } + + $deploymentId = $function->getAttribute('deployment', $function->getAttribute('deploymentId', $document->getAttribute('deploymentId', ''))); $deploymentInternalId = $function->getAttribute('deploymentInternalId', $document->getAttribute('deploymentInternalId', '')); $document @@ -382,16 +412,23 @@ class V22 extends Migration 2. Fill "deploymentCreatedAt" with deployment's "$createdAt" --- Fetch latestDeployment using find() 3. Fill latestDeploymentId with latestDeployment's "$id" - 4. Fill latestDeploymentInternalId with latestDeployment's "$internalId" + 4. Fill latestDeploymentInternalId with latestDeployment's "$sequence" 5. Fill latestDeploymentCreatedAt with latestDeployment's "$createdAt" 6. Fill latestDeploymentStatus with latestDeployment's build's "status" */ - if ($document->getAttribute('deployment')) { - $document->setAttribute('deploymentId', $document->getAttribute('deployment', $document->getAttribute('deploymentId', ''))); + if (empty($document->getAttribute('deployment'))) { + break; } + $document->setAttribute('deploymentId', $document->getAttribute('deployment', $document->getAttribute('deploymentId', ''))); $deploymentId = $document->getAttribute('deploymentId'); $deployment = $this->dbForProject->getDocument('deployments', $deploymentId); + + if ($deployment->isEmpty()) { + Console::warning("Deployment \"{$deploymentId}\" not found for function \"{$document->getId()}\""); + break; + } + $document->setAttribute('deploymentCreatedAt', $deployment->getCreatedAt()); $latestDeployment = $this->dbForProject->findOne('deployments', [ @@ -400,11 +437,21 @@ class V22 extends Migration Query::equal('resourceType', ['functions']), ]); + if ($latestDeployment->isEmpty()) { + Console::warning("Latest deployment not found for function \"{$document->getId()}\""); + break; + } + $latestBuild = $this->dbForProject->getDocument('builds', $latestDeployment->getAttribute('buildId', '')); + if ($latestBuild->isEmpty()) { + Console::warning("Build \"{$latestDeployment->getAttribute('buildId')}\" not found for deployment \"{$latestDeployment->getId()}\""); + break; + } + $document ->setAttribute('latestDeploymentId', $latestDeployment->getId()) - ->setAttribute('latestDeploymentInternalId', $latestDeployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $latestDeployment->getSequence()) ->setAttribute('latestDeploymentCreatedAt', $latestDeployment->getCreatedAt()) ->setAttribute('latestDeploymentStatus', $latestBuild->getAttribute('status', $document->getAttribute('latestDeploymentStatus', ''))); break; @@ -468,7 +515,7 @@ class V22 extends Migration private function cleanCollections(): void { - $projectInternalId = $this->project->getInternalId(); + $projectInternalId = $this->project->getSequence(); $collectionType = match ($projectInternalId) { 'console' => 'console', diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index 47529a142b..fe4a267cd7 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -4,7 +4,6 @@ namespace Appwrite\Platform\Modules\Compute; use Appwrite\Event\Build; use Appwrite\Extend\Exception; -use Appwrite\Query; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; @@ -69,13 +68,13 @@ class Base extends Action Permission::delete(Role::any()), ], 'resourceId' => $function->getId(), - 'resourceInternalId' => $function->getInternalId(), + 'resourceInternalId' => $function->getSequence(), 'resourceType' => 'functions', 'entrypoint' => $entrypoint, 'buildCommands' => $function->getAttribute('commands', ''), 'type' => 'vcs', 'installationId' => $installation->getId(), - 'installationInternalId' => $installation->getInternalId(), + 'installationInternalId' => $installation->getSequence(), 'providerRepositoryId' => $providerRepositoryId, 'repositoryId' => $function->getAttribute('repositoryId', ''), 'repositoryInternalId' => $function->getAttribute('repositoryInternalId', ''), @@ -95,7 +94,7 @@ class Base extends Action $function = $function ->setAttribute('latestDeploymentId', $deployment->getId()) - ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getSequence()) ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('functions', $function->getId(), $function); @@ -166,7 +165,7 @@ class Base extends Action Permission::delete(Role::any()), ], 'resourceId' => $site->getId(), - 'resourceInternalId' => $site->getInternalId(), + 'resourceInternalId' => $site->getSequence(), 'resourceType' => 'sites', 'buildCommands' => implode(' && ', $commands), 'buildOutput' => $site->getAttribute('outputDirectory', ''), @@ -174,7 +173,7 @@ class Base extends Action 'fallbackFile' => $site->getAttribute('fallbackFile', ''), 'type' => 'vcs', 'installationId' => $installation->getId(), - 'installationInternalId' => $installation->getInternalId(), + 'installationInternalId' => $installation->getSequence(), 'providerRepositoryId' => $providerRepositoryId, 'repositoryId' => $site->getAttribute('repositoryId', ''), 'repositoryInternalId' => $site->getAttribute('repositoryInternalId', ''), @@ -194,7 +193,7 @@ class Base extends Action $site = $site ->setAttribute('latestDeploymentId', $deployment->getId()) - ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getSequence()) ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('sites', $site->getId(), $site); @@ -209,15 +208,15 @@ class Base extends Action fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'domain' => $domain, 'trigger' => 'deployment', 'type' => 'deployment', 'deploymentId' => $deployment->getId(), - 'deploymentInternalId' => $deployment->getInternalId(), + 'deploymentInternalId' => $deployment->getSequence(), 'deploymentResourceType' => 'site', 'deploymentResourceId' => $site->getId(), - 'deploymentResourceInternalId' => $site->getInternalId(), + 'deploymentResourceInternalId' => $site->getSequence(), 'deploymentVcsProviderBranch' => $providerBranch, 'status' => 'verified', 'certificateId' => '', @@ -235,38 +234,4 @@ class Base extends Action return $deployment; } - - protected function listRules(Document $project, array $queries, Database $database, callable $callback): void - { - $limit = 100; - $cursor = null; - - do { - $queries = \array_merge([ - Query::limit($limit), - Query::equal("projectInternalId", [$project->getInternalId()]) - ], $queries); - - if ($cursor !== null) { - $queries[] = Query::cursorAfter($cursor); - } - - $results = $database->find('rules', $queries); - - $total = \count($results); - if ($total > 0) { - $cursor = $results[$total - 1]; - } - - if ($total < $limit) { - $cursor = null; - } - - foreach ($results as $document) { - if (is_callable($callback)) { - $callback($document); - } - } - } while (!\is_null($cursor)); - } } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php index 54894f2caa..007cea0252 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php @@ -86,6 +86,7 @@ class Create extends Action ->inject('deviceForFunctions') ->inject('deviceForLocal') ->inject('queueForBuilds') + ->inject('plan') ->callback([$this, 'action']); } @@ -102,7 +103,8 @@ class Create extends Action Document $project, Device $deviceForFunctions, Device $deviceForLocal, - Build $queueForBuilds + Build $queueForBuilds, + array $plan ) { $activate = \strval($activate) === 'true' || \strval($activate) === '1'; @@ -135,8 +137,14 @@ class Create extends Action throw new Exception(Exception::STORAGE_FILE_EMPTY, 'No file sent'); } + $functionSizeLimit = (int) System::getEnv('_APP_COMPUTE_SIZE_LIMIT', '30000000'); + + if (isset($plan['deploymentSize'])) { + $functionSizeLimit = $plan['deploymentSize'] * 1000 * 1000; + } + $fileExt = new FileExt([FileExt::TYPE_GZIP]); - $fileSizeValidator = new FileSize(System::getEnv('_APP_COMPUTE_SIZE_LIMIT', '30000000')); + $fileSizeValidator = new FileSize($functionSizeLimit); $upload = new Upload(); // Make sure we handle a single file and multiple files the same way @@ -174,7 +182,7 @@ class Create extends Action } } - if (!$fileSizeValidator->isValid($fileSize)) { // Check if file size is exceeding allowed limit + if (!$fileSizeValidator->isValid($fileSize) && $functionSizeLimit !== 0) { // Check if file size is exceeding allowed limit throw new Exception(Exception::STORAGE_INVALID_FILE_SIZE); } @@ -229,7 +237,7 @@ class Create extends Action Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'resourceInternalId' => $function->getInternalId(), + 'resourceInternalId' => $function->getSequence(), 'resourceId' => $function->getId(), 'resourceType' => 'functions', 'entrypoint' => $entrypoint, @@ -244,7 +252,7 @@ class Create extends Action $function = $function ->setAttribute('latestDeploymentId', $deployment->getId()) - ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getSequence()) ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('functions', $function->getId(), $function); @@ -266,7 +274,7 @@ class Create extends Action Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'resourceInternalId' => $function->getInternalId(), + 'resourceInternalId' => $function->getSequence(), 'resourceId' => $function->getId(), 'resourceType' => 'functions', 'entrypoint' => $entrypoint, @@ -283,7 +291,7 @@ class Create extends Action $function = $function ->setAttribute('latestDeploymentId', $deployment->getId()) - ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getSequence()) ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('functions', $function->getId(), $function); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Delete.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Delete.php index 84878055d8..912e12bdc1 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Delete.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Delete.php @@ -101,7 +101,7 @@ class Delete extends Action if ($function->getAttribute('latestDeploymentId') === $deployment->getId()) { $latestDeployment = $dbForProject->findOne('deployments', [ Query::equal('resourceType', ['functions']), - Query::equal('resourceInternalId', [$function->getInternalId()]), + Query::equal('resourceInternalId', [$function->getSequence()]), Query::orderDesc('$createdAt'), ]); $function = $dbForProject->updateDocument( @@ -109,7 +109,7 @@ class Delete extends Action $function->getId(), $function ->setAttribute('latestDeploymentCreatedAt', $latestDeployment->isEmpty() ? '' : $latestDeployment->getCreatedAt()) - ->setAttribute('latestDeploymentInternalId', $latestDeployment->isEmpty() ? '' : $latestDeployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $latestDeployment->isEmpty() ? '' : $latestDeployment->getSequence()) ->setAttribute('latestDeploymentId', $latestDeployment->isEmpty() ? '' : $latestDeployment->getId()) ->setAttribute('latestDeploymentStatus', $latestDeployment->isEmpty() ? '' : $latestDeployment->getAttribute('status', '')) ); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Download/Get.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Download/Get.php index 9456ff13a6..1993db4cf5 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Download/Get.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Download/Get.php @@ -105,9 +105,11 @@ class Get extends Action $response ->setContentType('application/gzip') - ->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days + ->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate') + ->addHeader('Expires', '0') + ->addHeader('Pragma', 'no-cache') ->addHeader('X-Peak', \memory_get_peak_usage()) - ->addHeader('Content-Disposition', 'attachment; filename="' . $deploymentId . '.tar.gz"'); + ->addHeader('Content-Disposition', 'attachment; filename="' . $deploymentId . '-' . $type . '.tar.gz"'); $size = $device->getFileSize($path); $rangeHeader = $request->getHeader('range'); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Duplicate/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Duplicate/Create.php index 8f739dd37f..0a07440dff 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Duplicate/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Duplicate/Create.php @@ -96,9 +96,9 @@ class Create extends Action $destination = $deviceForFunctions->getPath($deploymentId . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION)); $deviceForFunctions->transfer($path, $destination, $deviceForFunctions); - $deployment->removeAttribute('$internalId'); + $deployment->removeAttribute('$sequence'); $deployment = $dbForProject->createDocument('deployments', $deployment->setAttributes([ - '$internalId' => '', + '$sequence' => '', '$id' => $deploymentId, 'sourcePath' => $destination, 'totalSize' => $deployment->getAttribute('sourceSize', 0), @@ -115,7 +115,7 @@ class Create extends Action $function = $function ->setAttribute('latestDeploymentId', $deployment->getId()) - ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getSequence()) ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('functions', $function->getId(), $function); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Status/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Status/Update.php index c5436d1d05..4c924a64d1 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Status/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Status/Update.php @@ -97,7 +97,7 @@ class Update extends Action 'status' => 'canceled' ])); - if ($deployment->getInternalId() === $function->getAttribute('latestDeploymentInternalId', '')) { + if ($deployment->getSequence() === $function->getAttribute('latestDeploymentInternalId', '')) { $function = $function->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('functions', $function->getId(), $function); } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php index 55230bd4a5..f1bd4b71e4 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php @@ -142,7 +142,7 @@ class Create extends Base Permission::delete(Role::any()), ], 'resourceId' => $function->getId(), - 'resourceInternalId' => $function->getInternalId(), + 'resourceInternalId' => $function->getSequence(), 'resourceType' => 'functions', 'entrypoint' => $function->getAttribute('entrypoint', ''), 'buildCommands' => $function->getAttribute('commands', ''), @@ -152,7 +152,7 @@ class Create extends Base $function = $function ->setAttribute('latestDeploymentId', $deployment->getId()) - ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getSequence()) ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('functions', $function->getId(), $function); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/XList.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/XList.php index 38f1f38e89..2ffcfc5d11 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/XList.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/XList.php @@ -84,7 +84,7 @@ class XList extends Action } // Set resource queries - $queries[] = Query::equal('resourceInternalId', [$function->getInternalId()]); + $queries[] = Query::equal('resourceInternalId', [$function->getSequence()]); $queries[] = Query::equal('resourceType', ['functions']); /** diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php index fd1b2076a8..0aec31e5fd 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php @@ -253,10 +253,10 @@ class Create extends Base $execution = new Document([ '$id' => $executionId, '$permissions' => !$user->isEmpty() ? [Permission::read(Role::user($user->getId()))] : [], - 'resourceInternalId' => $function->getInternalId(), + 'resourceInternalId' => $function->getSequence(), 'resourceId' => $function->getId(), 'resourceType' => 'functions', - 'deploymentInternalId' => $deployment->getInternalId(), + 'deploymentInternalId' => $deployment->getSequence(), 'deploymentId' => $deployment->getId(), 'trigger' => (!is_null($scheduledAt)) ? 'schedule' : 'http', 'status' => $status, // waiting / processing / completed / failed / scheduled @@ -305,7 +305,7 @@ class Create extends Base 'region' => $project->getAttribute('region'), 'resourceType' => ScheduleExecutions::getSupportedResource(), 'resourceId' => $execution->getId(), - 'resourceInternalId' => $execution->getInternalId(), + 'resourceInternalId' => $execution->getSequence(), 'resourceUpdatedAt' => DateTime::now(), 'projectId' => $project->getId(), 'schedule' => $scheduledAt, @@ -315,7 +315,7 @@ class Create extends Base $execution = $execution ->setAttribute('scheduleId', $schedule->getId()) - ->setAttribute('scheduleInternalId', $schedule->getInternalId()) + ->setAttribute('scheduleInternalId', $schedule->getSequence()) ->setAttribute('scheduledAt', $scheduledAt); $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); @@ -439,13 +439,13 @@ class Create extends Base $queueForStatsUsage ->addMetric(METRIC_EXECUTIONS, 1) ->addMetric(str_replace(['{resourceType}'], [RESOURCE_TYPE_FUNCTIONS], METRIC_RESOURCE_TYPE_EXECUTIONS), 1) - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS), 1) + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS), 1) ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project ->addMetric(str_replace(['{resourceType}'], [RESOURCE_TYPE_FUNCTIONS], METRIC_RESOURCE_TYPE_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function ->addMetric(METRIC_EXECUTIONS_MB_SECONDS, (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) ->addMetric(str_replace(['{resourceType}'], [RESOURCE_TYPE_FUNCTIONS], METRIC_RESOURCE_TYPE_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) ; $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Delete.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Delete.php index cd85b5e534..8a3d5f2a49 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Delete.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Delete.php @@ -84,7 +84,7 @@ class Delete extends Base throw new Exception(Exception::EXECUTION_NOT_FOUND); } - if ($execution->getAttribute('resourceType') !== 'functions' && $execution->getAttribute('resourceInternalId') !== $function->getInternalId()) { + if ($execution->getAttribute('resourceType') !== 'functions' && $execution->getAttribute('resourceInternalId') !== $function->getSequence()) { throw new Exception(Exception::EXECUTION_NOT_FOUND); } $status = $execution->getAttribute('status'); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php index 892ce30f47..659682ab55 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php @@ -72,7 +72,7 @@ class Get extends Base $execution = $dbForProject->getDocument('executions', $executionId); - if ($execution->getAttribute('resourceType') !== 'functions' || $execution->getAttribute('resourceInternalId') !== $function->getInternalId()) { + if ($execution->getAttribute('resourceType') !== 'functions' || $execution->getAttribute('resourceInternalId') !== $function->getSequence()) { throw new Exception(Exception::EXECUTION_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/XList.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/XList.php index a31e95b1c8..91683c915b 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/XList.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/XList.php @@ -83,7 +83,7 @@ class XList extends Base } // Set internal queries - $queries[] = Query::equal('resourceInternalId', [$function->getInternalId()]); + $queries[] = Query::equal('resourceInternalId', [$function->getSequence()]); $queries[] = Query::equal('resourceType', ['functions']); /** diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php index fd8da657eb..c644c681d8 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php @@ -222,7 +222,7 @@ class Create extends Base 'search' => implode(' ', [$functionId, $name, $runtime]), 'version' => 'v5', 'installationId' => $installation->getId(), - 'installationInternalId' => $installation->getInternalId(), + 'installationInternalId' => $installation->getSequence(), 'providerRepositoryId' => $providerRepositoryId, 'repositoryId' => '', 'repositoryInternalId' => '', @@ -237,7 +237,7 @@ class Create extends Base 'region' => $project->getAttribute('region'), 'resourceType' => 'function', 'resourceId' => $function->getId(), - 'resourceInternalId' => $function->getInternalId(), + 'resourceInternalId' => $function->getSequence(), 'resourceUpdatedAt' => DateTime::now(), 'projectId' => $project->getId(), 'schedule' => $function->getAttribute('schedule'), @@ -246,7 +246,7 @@ class Create extends Base ); $function->setAttribute('scheduleId', $schedule->getId()); - $function->setAttribute('scheduleInternalId', $schedule->getInternalId()); + $function->setAttribute('scheduleInternalId', $schedule->getSequence()); // Git connect logic if (!empty($providerRepositoryId)) { @@ -262,18 +262,18 @@ class Create extends Base Permission::delete(Role::team(ID::custom($teamId), 'developer')), ], 'installationId' => $installation->getId(), - 'installationInternalId' => $installation->getInternalId(), + 'installationInternalId' => $installation->getSequence(), 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'providerRepositoryId' => $providerRepositoryId, 'resourceId' => $function->getId(), - 'resourceInternalId' => $function->getInternalId(), + 'resourceInternalId' => $function->getSequence(), 'resourceType' => 'function', 'providerPullRequestIds' => [] ])); $function->setAttribute('repositoryId', $repository->getId()); - $function->setAttribute('repositoryInternalId', $repository->getInternalId()); + $function->setAttribute('repositoryInternalId', $repository->getSequence()); } $function = $dbForProject->updateDocument('functions', $function->getId(), $function); @@ -316,7 +316,7 @@ class Create extends Base $function = $function ->setAttribute('latestDeploymentId', $deployment->getId()) - ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getSequence()) ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('functions', $function->getId(), $function); @@ -331,7 +331,7 @@ class Create extends Base Permission::delete(Role::any()), ], 'resourceId' => $function->getId(), - 'resourceInternalId' => $function->getInternalId(), + 'resourceInternalId' => $function->getSequence(), 'resourceType' => 'functions', 'entrypoint' => $function->getAttribute('entrypoint', ''), 'buildCommands' => $function->getAttribute('commands', ''), @@ -341,7 +341,7 @@ class Create extends Base $function = $function ->setAttribute('latestDeploymentId', $deployment->getId()) - ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getSequence()) ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('functions', $function->getId(), $function); @@ -364,16 +364,16 @@ class Create extends Base fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'domain' => $domain, 'status' => 'verified', 'type' => 'deployment', 'trigger' => 'manual', 'deploymentId' => !isset($deployment) || $deployment->isEmpty() ? '' : $deployment->getId(), - 'deploymentInternalId' => !isset($deployment) || $deployment->isEmpty() ? '' : $deployment->getInternalId(), + 'deploymentInternalId' => !isset($deployment) || $deployment->isEmpty() ? '' : $deployment->getSequence(), 'deploymentResourceType' => 'function', 'deploymentResourceId' => $function->getId(), - 'deploymentResourceInternalId' => $function->getInternalId(), + 'deploymentResourceInternalId' => $function->getSequence(), 'deploymentVcsProviderBranch' => '', 'certificateId' => '', 'search' => implode(' ', [$ruleId, $domain]), diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Deployment/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Deployment/Update.php index 6de71cfae6..23e2fc3b91 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Deployment/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Deployment/Update.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Modules\Functions\Http\Functions\Deployment; use Appwrite\Event\Event; use Appwrite\Extend\Exception; use Appwrite\Platform\Modules\Compute\Base; -use Appwrite\Query; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; @@ -13,6 +12,7 @@ use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; +use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; @@ -89,10 +89,8 @@ class Update extends Base throw new Exception(Exception::BUILD_NOT_READY); } - $oldDeploymentInternalId = $function->getAttribute('deploymentInternalId', ''); - $function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [ - 'deploymentInternalId' => $deployment->getInternalId(), + 'deploymentInternalId' => $deployment->getSequence(), 'deploymentId' => $deployment->getId(), 'deploymentCreatedAt' => $deployment->getCreatedAt(), ]))); @@ -106,24 +104,21 @@ class Update extends Base Authorization::skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule)); $queries = [ - Query::equal('trigger', 'manual'), - Query::equal("type", ["deployment"]), - Query::equal("deploymentResourceType", ["function"]), - Query::equal("deploymentResourceInternalId", [$function->getInternalId()]), + Query::equal('trigger', ['manual']), + Query::equal('type', ['deployment']), + Query::equal('deploymentResourceType', ['function']), + Query::equal('deploymentResourceInternalId', [$function->getSequence()]), + Query::equal('deploymentVcsProviderBranch', ['']), + Query::equal('projectInternalId', [$project->getSequence()]) ]; - if (empty($oldDeploymentInternalId)) { - $queries[] = Query::equal("deploymentInternalId", [""]); - } else { - $queries[] = Query::equal("deploymentInternalId", [$oldDeploymentInternalId]); - } - - $this->listRules($project, $queries, $dbForPlatform, function (Document $rule) use ($dbForPlatform, $deployment) { + Authorization::skip(fn () => $dbForPlatform->foreach('rules', function (Document $rule) use ($dbForPlatform, $deployment) { $rule = $rule ->setAttribute('deploymentId', $deployment->getId()) - ->setAttribute('deploymentInternalId', $deployment->getInternalId()); - $dbForPlatform->updateDocument('rules', $rule->getId(), $rule); - }); + ->setAttribute('deploymentInternalId', $deployment->getSequence()); + + Authorization::skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), $rule)); + }, $queries)); $queueForEvents ->setParam('functionId', $function->getId()) diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php index a19ec1e278..2bfe8b1344 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php @@ -175,8 +175,8 @@ class Update extends Base // Git disconnect logic. Disconnecting only when providerRepositoryId is empty, allowing for continue updates without disconnecting git if ($isConnected && ($providerRepositoryId !== null && empty($providerRepositoryId))) { $repositories = $dbForPlatform->find('repositories', [ - Query::equal('projectInternalId', [$project->getInternalId()]), - Query::equal('resourceInternalId', [$function->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), + Query::equal('resourceInternalId', [$function->getSequence()]), Query::equal('resourceType', ['function']), Query::limit(100), ]); @@ -208,18 +208,18 @@ class Update extends Base Permission::delete(Role::team(ID::custom($teamId), 'developer')), ], 'installationId' => $installation->getId(), - 'installationInternalId' => $installation->getInternalId(), + 'installationInternalId' => $installation->getSequence(), 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'providerRepositoryId' => $providerRepositoryId, 'resourceId' => $function->getId(), - 'resourceInternalId' => $function->getInternalId(), + 'resourceInternalId' => $function->getSequence(), 'resourceType' => 'function', 'providerPullRequestIds' => [] ])); $repositoryId = $repository->getId(); - $repositoryInternalId = $repository->getInternalId(); + $repositoryInternalId = $repository->getSequence(); } $live = true; @@ -260,7 +260,7 @@ class Update extends Base 'commands' => $commands, 'scopes' => $scopes, 'installationId' => $installation->getId(), - 'installationInternalId' => $installation->getInternalId(), + 'installationInternalId' => $installation->getSequence(), 'providerRepositoryId' => $providerRepositoryId, 'repositoryId' => $repositoryId, 'repositoryInternalId' => $repositoryInternalId, diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Usage/Get.php b/src/Appwrite/Platform/Modules/Functions/Http/Usage/Get.php index e0d659c9ba..947da4cd37 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Usage/Get.php @@ -70,17 +70,17 @@ class Get extends Base $stats = $usage = []; $days = $periods[$range]; $metrics = [ - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_SUCCESS), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_FAILED), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_SUCCESS), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_FAILED), ]; Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Create.php index 9300524c64..ee892fe1ed 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Create.php @@ -96,7 +96,7 @@ class Create extends Base Permission::delete(Role::team(ID::custom($teamId), 'owner')), Permission::delete(Role::team(ID::custom($teamId), 'developer')), ], - 'resourceInternalId' => $function->getInternalId(), + 'resourceInternalId' => $function->getSequence(), 'resourceId' => $function->getId(), 'resourceType' => 'function', 'key' => $key, diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Delete.php b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Delete.php index 3d6bfebcb9..dda1f97f6b 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Delete.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Delete.php @@ -74,7 +74,7 @@ class Delete extends Base } $variable = $dbForProject->getDocument('variables', $variableId); - if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $function->getInternalId() || $variable->getAttribute('resourceType') !== 'function') { + if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $function->getSequence() || $variable->getAttribute('resourceType') !== 'function') { throw new Exception(Exception::VARIABLE_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Get.php b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Get.php index 70cc66219a..98119c4a66 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Get.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Get.php @@ -68,7 +68,7 @@ class Get extends Base if ( $variable === false || $variable->isEmpty() || - $variable->getAttribute('resourceInternalId') !== $function->getInternalId() || + $variable->getAttribute('resourceInternalId') !== $function->getSequence() || $variable->getAttribute('resourceType') !== 'function' ) { throw new Exception(Exception::VARIABLE_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Update.php index 4102022267..7a6acf88e3 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Update.php @@ -81,7 +81,7 @@ class Update extends Base } $variable = $dbForProject->getDocument('variables', $variableId); - if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $function->getInternalId() || $variable->getAttribute('resourceType') !== 'function') { + if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $function->getSequence() || $variable->getAttribute('resourceType') !== 'function') { throw new Exception(Exception::VARIABLE_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index 8c83c28618..048d9f83b8 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -57,6 +57,7 @@ class Builds extends Action { $this ->desc('Builds worker') + ->groups(['builds']) ->inject('message') ->inject('project') ->inject('dbForPlatform') @@ -73,6 +74,7 @@ class Builds extends Action ->inject('deviceForFiles') ->inject('log') ->inject('executor') + ->inject('plan') ->callback([$this, 'action']); } @@ -92,6 +94,7 @@ class Builds extends Action * @param Device $deviceForFiles * @param Log $log * @param Executor $executor + * @param array $plan * @return void * @throws \Utopia\Database\Exception */ @@ -111,7 +114,8 @@ class Builds extends Action callable $isResourceBlocked, Device $deviceForFiles, Log $log, - Executor $executor + Executor $executor, + array $plan ): void { $payload = $message->getPayload() ?? []; @@ -150,7 +154,8 @@ class Builds extends Action $template, $isResourceBlocked, $log, - $executor + $executor, + $plan ); break; @@ -177,6 +182,7 @@ class Builds extends Action * @param Document $template * @param Log $log * @param Executor $executor + * @param array $plan * @return void * @throws \Utopia\Database\Exception * @@ -200,7 +206,8 @@ class Builds extends Action Document $template, callable $isResourceBlocked, Log $log, - Executor $executor + Executor $executor, + array $plan ): void { $resourceKey = match ($resource->getCollection()) { 'functions' => 'functionId', @@ -267,7 +274,7 @@ class Builds extends Action $deployment->setAttribute('status', 'processing'); $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); - if ($deployment->getInternalId() === $resource->getAttribute('latestDeploymentInternalId', '')) { + if ($deployment->getSequence() === $resource->getAttribute('latestDeploymentInternalId', '')) { $resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource); } @@ -476,8 +483,12 @@ class Builds extends Action $directorySize = $localDevice->getDirectorySize($tmpDirectory); $sizeLimit = (int)System::getEnv('_APP_COMPUTE_SIZE_LIMIT', '30000000'); - if ($directorySize > $sizeLimit) { - throw new \Exception('Repository directory size should be less than ' . number_format($sizeLimit / 1048576, 2) . ' MBs.'); + if (isset($plan['deploymentSize'])) { + $sizeLimit = (int) $plan['deploymentSize'] * 1000 * 1000; + } + + if ($directorySize > $sizeLimit && $sizeLimit !== 0) { + throw new \Exception('Repository directory size should be less than ' . number_format($sizeLimit / (1000 * 1000), 2) . ' MBs.'); } Console::execute('find ' . \escapeshellarg($tmpDirectory) . ' -type d -name ".git" -exec rm -rf {} +', '', $stdout, $stderr); @@ -513,7 +524,7 @@ class Builds extends Action $deployment->setAttribute('status', 'building'); $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); - if ($deployment->getInternalId() === $resource->getAttribute('latestDeploymentInternalId', '')) { + if ($deployment->getSequence() === $resource->getAttribute('latestDeploymentInternalId', '')) { $resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource); } @@ -803,8 +814,11 @@ class Builds extends Action $durationEnd = \microtime(true); $buildSizeLimit = (int)System::getEnv('_APP_COMPUTE_BUILD_SIZE_LIMIT', '2000000000'); - if ($response['size'] > $buildSizeLimit) { - throw new \Exception('Build size should be less than ' . number_format($buildSizeLimit / 1048576, 2) . ' MBs.'); + if (isset($plan['buildSize'])) { + $buildSizeLimit = $plan['buildSize'] * 1000 * 1000; + } + if ($response['size'] > $buildSizeLimit && $buildSizeLimit !== 0) { + throw new \Exception('Build size should be less than ' . number_format($buildSizeLimit / (1000 * 1000), 2) . ' MBs.'); } /** Update the build document */ @@ -863,6 +877,10 @@ class Builds extends Action } } + $deployment->setAttribute('buildLogs', $logs); + + $this->afterBuildSuccess($dbForProject, $deployment); + $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); $queueForRealtime @@ -873,9 +891,9 @@ class Builds extends Action if ($resource->getCollection() === 'sites') { try { $rule = Authorization::skip(fn () => $dbForPlatform->findOne('rules', [ - Query::equal("projectInternalId", [$project->getInternalId()]), + Query::equal("projectInternalId", [$project->getSequence()]), Query::equal("type", ["deployment"]), - Query::equal('deploymentInternalId', [$deployment->getInternalId()]), + Query::equal('deploymentInternalId', [$deployment->getSequence()]), ])); if ($rule->isEmpty()) { @@ -914,9 +932,9 @@ class Builds extends Action str_replace(["{resourceType}"], [RESOURCE_TYPE_SITES], METRIC_RESOURCE_TYPE_EXECUTIONS), str_replace(["{resourceType}"], [RESOURCE_TYPE_SITES], METRIC_RESOURCE_TYPE_EXECUTIONS_COMPUTE), str_replace(["{resourceType}"], [RESOURCE_TYPE_SITES], METRIC_RESOURCE_TYPE_EXECUTIONS_MB_SECONDS), - str_replace(["{resourceType}", "{resourceInternalId}"], [RESOURCE_TYPE_SITES, $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS), - str_replace(["{resourceType}", "{resourceInternalId}"], [RESOURCE_TYPE_SITES, $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE), - str_replace(["{resourceType}", "{resourceInternalId}"], [RESOURCE_TYPE_SITES, $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), + str_replace(["{resourceType}", "{resourceInternalId}"], [RESOURCE_TYPE_SITES, $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS), + str_replace(["{resourceType}", "{resourceInternalId}"], [RESOURCE_TYPE_SITES, $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE), + str_replace(["{resourceType}", "{resourceInternalId}"], [RESOURCE_TYPE_SITES, $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), ], 'bannerDisabled' => true, 'projectCheckDisabled' => true, @@ -986,7 +1004,7 @@ class Builds extends Action Permission::read(Role::team(ID::custom($teamId))), ], 'bucketId' => $bucket->getId(), - 'bucketInternalId' => $bucket->getInternalId(), + 'bucketInternalId' => $bucket->getSequence(), 'name' => $fileName, 'path' => $path, 'signature' => $deviceForFiles->getFileHash($path), @@ -1005,7 +1023,7 @@ class Builds extends Action 'metadata' => ['content_type' => $deviceForFiles->getFileMimeType($path)], ]); - Authorization::skip(fn () => $dbForPlatform->createDocument('bucket_' . $bucket->getInternalId(), $file)); + Authorization::skip(fn () => $dbForPlatform->createDocument('bucket_' . $bucket->getSequence(), $file)); $deployment->setAttribute($key, $fileId); } @@ -1041,7 +1059,7 @@ class Builds extends Action $deployment->setAttribute('status', 'ready'); $deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment); - if ($deployment->getInternalId() === $resource->getAttribute('latestDeploymentInternalId', '')) { + if ($deployment->getSequence() === $resource->getAttribute('latestDeploymentInternalId', '')) { $resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource); } @@ -1061,65 +1079,51 @@ class Builds extends Action $resource->setAttribute('live', true); switch ($resource->getCollection()) { case 'functions': - $oldDeploymentInternalId = $resource->getAttribute('deploymentInternalId', ''); - $resource->setAttribute('deploymentId', $deployment->getId()); - $resource->setAttribute('deploymentInternalId', $deployment->getInternalId()); + $resource->setAttribute('deploymentInternalId', $deployment->getSequence()); $resource->setAttribute('deploymentCreatedAt', $deployment->getCreatedAt()); $resource = $dbForProject->updateDocument('functions', $resource->getId(), $resource); $queries = [ - Query::equal("projectInternalId", [$project->getInternalId()]), - Query::equal("type", ["deployment"]), - Query::equal("deploymentResourceInternalId", [$resource->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), + Query::equal('type', ['deployment']), + Query::equal('deploymentResourceInternalId', [$resource->getSequence()]), Query::equal('deploymentResourceType', ['function']), Query::equal('trigger', ['manual']), + Query::equal('deploymentVcsProviderBranch', ['']), ]; - if (empty($oldDeploymentInternalId)) { - $queries[] = Query::equal("deploymentInternalId", [""]); - } else { - $queries[] = Query::equal("deploymentInternalId", [$oldDeploymentInternalId]); - } - $rulesUpdated = false; - $this->listRules($project, $queries, $dbForPlatform, function (Document $rule) use ($dbForPlatform, $deployment, &$rulesUpdated) { + $dbForPlatform->forEach('rules', function (Document $rule) use ($dbForPlatform, $deployment, &$rulesUpdated) { $rulesUpdated = true; $rule = $rule ->setAttribute('deploymentId', $deployment->getId()) - ->setAttribute('deploymentInternalId', $deployment->getInternalId()); + ->setAttribute('deploymentInternalId', $deployment->getSequence()); $dbForPlatform->updateDocument('rules', $rule->getId(), $rule); - }); + }, $queries); break; case 'sites': - $oldDeploymentInternalId = $resource->getAttribute('deploymentInternalId', ''); - $resource->setAttribute('deploymentId', $deployment->getId()); - $resource->setAttribute('deploymentInternalId', $deployment->getInternalId()); + $resource->setAttribute('deploymentInternalId', $deployment->getSequence()); $resource->setAttribute('deploymentScreenshotDark', $deployment->getAttribute('screenshotDark', '')); $resource->setAttribute('deploymentScreenshotLight', $deployment->getAttribute('screenshotLight', '')); $resource->setAttribute('deploymentCreatedAt', $deployment->getCreatedAt()); $resource = $dbForProject->updateDocument('sites', $resource->getId(), $resource); $queries = [ - Query::equal("projectInternalId", [$project->getInternalId()]), - Query::equal("type", ["deployment"]), - Query::equal("deploymentResourceInternalId", [$resource->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), + Query::equal('type', ['deployment']), + Query::equal('deploymentResourceInternalId', [$resource->getSequence()]), Query::equal('deploymentResourceType', ['site']), Query::equal('trigger', ['manual']), + Query::equal('deploymentVcsProviderBranch', ['']), ]; - if (empty($oldDeploymentInternalId)) { - $queries[] = Query::equal("deploymentInternalId", [""]); - } else { - $queries[] = Query::equal("deploymentInternalId", [$oldDeploymentInternalId]); - } - - $this->listRules($project, $queries, $dbForPlatform, function (Document $rule) use ($dbForPlatform, $deployment) { + $dbForPlatform->forEach('rules', function (Document $rule) use ($dbForPlatform, $deployment) { $rule = $rule ->setAttribute('deploymentId', $deployment->getId()) - ->setAttribute('deploymentInternalId', $deployment->getInternalId()); + ->setAttribute('deploymentInternalId', $deployment->getSequence()); $dbForPlatform->updateDocument('rules', $rule->getId(), $rule); - }); + }, $queries); break; } @@ -1130,22 +1134,28 @@ class Builds extends Action $branchName = $deployment->getAttribute('providerBranch'); if (!empty($branchName)) { $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - $domain = "branch-{$branchName}-{$resource->getId()}-{$project->getId()}.{$sitesDomain}"; + $branchPrefix = substr($branchName, 0, 16); + if (strlen($branchName) > 16) { + $remainingChars = substr($branchName, 16); + $branchPrefix .= '-' . substr(hash('sha256', $remainingChars), 0, 7); + } + $resourceProjectHash = substr(hash('sha256', $resource->getId() . $project->getId()), 0, 7); + $domain = "branch-{$branchPrefix}-{$resourceProjectHash}.{$sitesDomain}"; $ruleId = md5($domain); try { $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'domain' => $domain, 'type' => 'deployment', 'trigger' => 'deployment', 'deploymentId' => $deployment->getId(), - 'deploymentInternalId' => $deployment->getInternalId(), + 'deploymentInternalId' => $deployment->getSequence(), 'deploymentResourceType' => 'site', 'deploymentResourceId' => $deployment->getId(), - 'deploymentResourceInternalId' => $deployment->getInternalId(), + 'deploymentResourceInternalId' => $deployment->getSequence(), 'deploymentVcsProviderBranch' => $branchName, 'status' => 'verified', 'certificateId' => '', @@ -1157,23 +1167,25 @@ class Builds extends Action $rule = $dbForPlatform->getDocument('rules', $ruleId); $rule = $rule ->setAttribute('deploymentId', $deployment->getId()) - ->setAttribute('deploymentInternalId', $deployment->getInternalId()); + ->setAttribute('deploymentInternalId', $deployment->getSequence()); $dbForPlatform->updateDocument('rules', $rule->getId(), $rule); } - $this->listRules($project, [ - Query::equal("projectInternalId", [$project->getInternalId()]), - Query::equal("type", ["deployment"]), - Query::equal("deploymentResourceInternalId", [$resource->getInternalId()]), + $queries = [ + Query::equal('projectInternalId', [$project->getSequence()]), + Query::equal('type', ['deployment']), + Query::equal('deploymentResourceInternalId', [$resource->getSequence()]), Query::equal('deploymentResourceType', ['site']), - Query::equal("deploymentVcsProviderBranch", [$branchName]), - Query::equal("trigger", ['manual']), - ], $dbForPlatform, function (Document $rule) use ($dbForPlatform, $deployment) { + Query::equal('deploymentVcsProviderBranch', [$branchName]), + Query::equal('trigger', ['manual']), + ]; + + $dbForPlatform->foreach('rules', function (Document $rule) use ($dbForPlatform, $deployment) { $rule = $rule ->setAttribute('deploymentId', $deployment->getId()) - ->setAttribute('deploymentInternalId', $deployment->getInternalId()); + ->setAttribute('deploymentInternalId', $deployment->getSequence()); $dbForPlatform->updateDocument('rules', $rule->getId(), $rule); - }); + }, $queries); } } @@ -1218,16 +1230,25 @@ class Builds extends Action $message .= "\n" . $error; } + // Combine with previous logs if deployment got past build process + $previousLogs = ''; + if (!empty($deployment->getAttribute('buildEndedAt', ''))) { + $previousLogs = $deployment->getAttribute('buildLogs', ''); + if (!empty($previousLogs)) { + $message = $previousLogs . "\n" . $message; + } + } + $endTime = DateTime::now(); $durationEnd = \microtime(true); $deployment->setAttribute('buildEndedAt', $endTime); $deployment->setAttribute('buildDuration', \intval(\ceil($durationEnd - $durationStart))); $deployment->setAttribute('status', 'failed'); - $deployment->setAttribute('buildLogs', $message); + $deployment->setAttribute('buildLogs', $message); $deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment); - if ($deployment->getInternalId() === $resource->getAttribute('latestDeploymentInternalId', '')) { + if ($deployment->getSequence() === $resource->getAttribute('latestDeploymentInternalId', '')) { $resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource); } @@ -1262,8 +1283,8 @@ class Builds extends Action ->addMetric(METRIC_BUILDS_COMPUTE_SUCCESS, (int)$deployment->getAttribute('buildDuration', 0) * 1000) ->addMetric(str_replace(['{resourceType}'], [$deployment->getAttribute('resourceType')], METRIC_RESOURCE_TYPE_BUILDS_SUCCESS), 1) // per function ->addMetric(str_replace(['{resourceType}'], [$deployment->getAttribute('resourceType')], METRIC_RESOURCE_TYPE_BUILDS_COMPUTE_SUCCESS), (int)$deployment->getAttribute('buildDuration', 0) * 1000) - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_SUCCESS), 1) // per function - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE_SUCCESS), (int)$deployment->getAttribute('buildDuration', 0) * 1000); + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_SUCCESS), 1) // per function + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE_SUCCESS), (int)$deployment->getAttribute('buildDuration', 0) * 1000); break; case 'failed': $queue @@ -1271,8 +1292,8 @@ class Builds extends Action ->addMetric(METRIC_BUILDS_COMPUTE_FAILED, (int)$deployment->getAttribute('buildDuration', 0) * 1000) ->addMetric(str_replace(['{resourceType}'], [$deployment->getAttribute('resourceType')], METRIC_RESOURCE_TYPE_BUILDS_FAILED), 1) // per function ->addMetric(str_replace(['{resourceType}'], [$deployment->getAttribute('resourceType')], METRIC_RESOURCE_TYPE_BUILDS_COMPUTE_FAILED), (int)$deployment->getAttribute('buildDuration', 0) * 1000) - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_FAILED), 1) // per function - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE_FAILED), (int)$deployment->getAttribute('buildDuration', 0) * 1000); + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_FAILED), 1) // per function + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE_FAILED), (int)$deployment->getAttribute('buildDuration', 0) * 1000); break; } @@ -1285,14 +1306,27 @@ class Builds extends Action ->addMetric(str_replace(['{resourceType}'], [$deployment->getAttribute('resourceType')], METRIC_RESOURCE_TYPE_BUILDS_STORAGE), $deployment->getAttribute('buildSize', 0)) ->addMetric(str_replace(['{resourceType}'], [$deployment->getAttribute('resourceType')], METRIC_RESOURCE_TYPE_BUILDS_COMPUTE), (int)$deployment->getAttribute('buildDuration', 0) * 1000) ->addMetric(str_replace(['{resourceType}'], [$deployment->getAttribute('resourceType')], METRIC_RESOURCE_TYPE_BUILDS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $deployment->getAttribute('buildDuration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS), 1) // per function - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), $deployment->getAttribute('buildSize', 0)) - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE), (int)$deployment->getAttribute('buildDuration', 0) * 1000) - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $deployment->getAttribute('buildDuration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS), 1) // per function + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), $deployment->getAttribute('buildSize', 0)) + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE), (int)$deployment->getAttribute('buildDuration', 0) * 1000) + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $deployment->getAttribute('buildDuration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) ->setProject($project) ->trigger(); } + /** + * Hook to run after build success + * + * @param Database $dbForProject + * @param Document $deployment + * @return void + */ + protected function afterBuildSuccess(Database $dbForProject, Document &$deployment): void + { + assert($dbForProject instanceof Database); + assert($deployment instanceof Document); + } + protected function getRuntime(Document $resource, string $version): array { $runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []); @@ -1446,9 +1480,9 @@ class Builds extends Action }; $rule = Authorization::skip(fn () => $dbForPlatform->findOne('rules', [ - Query::equal("projectInternalId", [$project->getInternalId()]), + Query::equal("projectInternalId", [$project->getSequence()]), Query::equal("type", ["deployment"]), - Query::equal("deploymentInternalId", [$deployment->getInternalId()]), + Query::equal("deploymentInternalId", [$deployment->getSequence()]), ])); $protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; @@ -1467,38 +1501,4 @@ class Builds extends Action } } } - - protected function listRules(Document $project, array $queries, Database $database, callable $callback): void - { - $limit = 100; - $cursor = null; - - do { - $queries = \array_merge([ - Query::limit($limit), - Query::equal("projectInternalId", [$project->getInternalId()]) - ], $queries); - - if ($cursor !== null) { - $queries[] = Query::cursorAfter($cursor); - } - - $results = $database->find('rules', $queries); - - $total = \count($results); - if ($total > 0) { - $cursor = $results[$total - 1]; - } - - if ($total < $limit) { - $cursor = null; - } - - foreach ($results as $document) { - if (is_callable($callback)) { - $callback($document); - } - } - } while (!\is_null($cursor)); - } } diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php index 43f7d4ac02..54ff189c20 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Create.php @@ -76,7 +76,7 @@ class Create extends Action Permission::update(Role::user($user->getId())), Permission::delete(Role::user($user->getId())), ], - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'projectId' => $project->getId(), 'name' => $name, 'expire' => $expire, diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php index f6cb966f50..eac42be5f0 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Delete.php @@ -63,7 +63,7 @@ class Delete extends Action $key = $dbForPlatform->getDocument('devKeys', $keyId); - if ($key === false || $key->isEmpty() || $key->getAttribute('projectInternalId') !== $project->getInternalId()) { + if ($key === false || $key->isEmpty() || $key->getAttribute('projectInternalId') !== $project->getSequence()) { throw new Exception(Exception::KEY_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php index bd472b26e7..6255976de4 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Get.php @@ -63,7 +63,7 @@ class Get extends Action $key = $dbForPlatform->getDocument('devKeys', $keyId); - if ($key === false || $key->isEmpty() || $key->getAttribute('projectInternalId') !== $project->getInternalId()) { + if ($key === false || $key->isEmpty() || $key->getAttribute('projectInternalId') !== $project->getSequence()) { throw new Exception(Exception::KEY_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php index d5e6839174..33b91eb0b6 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/Update.php @@ -65,7 +65,7 @@ class Update extends Action $key = $dbForPlatform->getDocument('devKeys', $keyId); - if ($key === false || $key->isEmpty() || $key->getAttribute('projectInternalId') !== $project->getInternalId()) { + if ($key === false || $key->isEmpty() || $key->getAttribute('projectInternalId') !== $project->getSequence()) { throw new Exception(Exception::KEY_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php index 864531d32d..73e79a783a 100644 --- a/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php +++ b/src/Appwrite/Platform/Modules/Projects/Http/DevKeys/XList.php @@ -71,7 +71,7 @@ class XList extends Action throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - $queries[] = Query::equal('projectInternalId', [$project->getInternalId()]); + $queries[] = Query::equal('projectInternalId', [$project->getSequence()]); $keys = $dbForPlatform->find('devKeys', $queries); diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php index 8ae48ab345..33eb5313a0 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/API/Create.php @@ -153,7 +153,7 @@ class Create extends Action $rule = new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'domain' => $domain->get(), 'status' => $status, 'type' => 'api', @@ -174,7 +174,7 @@ class Create extends Action $queueForCertificates ->setDomain(new Document([ 'domain' => $rule->getAttribute('domain'), - 'domainType' => 'api', + 'domainType' => $rule->getAttribute('deploymentResourceType', $rule->getAttribute('type')), ])) ->trigger(); } diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php index 15d58c7b29..0b68b80aa3 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Function/Create.php @@ -165,16 +165,16 @@ class Create extends Action $rule = new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'domain' => $domain->get(), 'status' => $status, 'type' => 'deployment', 'trigger' => 'manual', 'deploymentId' => $deployment->isEmpty() ? '' : $deployment->getId(), - 'deploymentInternalId' => $deployment->isEmpty() ? '' : $deployment->getInternalId(), + 'deploymentInternalId' => $deployment->isEmpty() ? '' : $deployment->getSequence(), 'deploymentResourceType' => 'function', 'deploymentResourceId' => $function->getId(), - 'deploymentResourceInternalId' => $function->getInternalId(), + 'deploymentResourceInternalId' => $function->getSequence(), 'deploymentVcsProviderBranch' => $branch, 'certificateId' => '', 'search' => implode(' ', [$ruleId, $domain->get(), $branch]), @@ -192,7 +192,7 @@ class Create extends Action $queueForCertificates ->setDomain(new Document([ 'domain' => $rule->getAttribute('domain'), - 'domainType' => 'function', + 'domainType' => $rule->getAttribute('deploymentResourceType', $rule->getAttribute('type')), ])) ->trigger(); } diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php index 0ff41e5ac2..60e8759e5c 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Redirect/Create.php @@ -14,6 +14,7 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate; use Utopia\Database\Helpers\ID; +use Utopia\Database\Validator\UID; use Utopia\Domains\Domain; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -65,15 +66,18 @@ class Create extends Action ->param('domain', null, new ValidatorDomain(), 'Domain name.') ->param('url', null, new URL(), 'Target URL of redirection') ->param('statusCode', null, new WhiteList([301, 302, 307, 308]), 'Status code of redirection') + ->param('resourceId', '', new UID(), 'ID of parent resource.') + ->param('resourceType', '', new WhiteList(['site', 'function']), 'Type of parent resource.') ->inject('response') ->inject('project') ->inject('queueForCertificates') ->inject('queueForEvents') ->inject('dbForPlatform') + ->inject('dbForProject') ->callback([$this, 'action']); } - public function action(string $domain, string $url, int $statusCode, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform) + public function action(string $domain, string $url, int $statusCode, string $resourceId, string $resourceType, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject) { $deniedDomains = [ 'localhost', @@ -116,6 +120,15 @@ class Create extends Action throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain may not start with http:// or https://.'); } + $collection = match ($resourceType) { + 'site' => 'sites', + 'function' => 'functions' + }; + $resource = $dbForProject->getDocument($collection, $resourceId); + if ($resource->isEmpty()) { + throw new Exception(Exception::RULE_RESOURCE_NOT_FOUND); + } + // TODO: @christyjacob remove once we migrate the rules in 1.7.x $ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain->get()) : ID::unique(); @@ -157,13 +170,16 @@ class Create extends Action $rule = new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'domain' => $domain->get(), 'status' => $status, 'type' => 'redirect', 'trigger' => 'manual', 'redirectUrl' => $url, 'redirectStatusCode' => $statusCode, + 'deploymentResourceType' => $resourceType, + 'deploymentResourceId' => $resource->getId(), + 'deploymentResourceInternalId' => $resource->getSequence(), 'certificateId' => '', 'search' => implode(' ', [$ruleId, $domain->get()]), 'owner' => $owner, @@ -180,7 +196,7 @@ class Create extends Action $queueForCertificates ->setDomain(new Document([ 'domain' => $rule->getAttribute('domain'), - 'domainType' => 'redirect', + 'domainType' => $rule->getAttribute('deploymentResourceType', $rule->getAttribute('type')), ])) ->trigger(); } diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php index f5f6628d68..7c2a640cdf 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Site/Create.php @@ -165,16 +165,16 @@ class Create extends Action $rule = new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'domain' => $domain->get(), 'status' => $status, 'type' => 'deployment', 'trigger' => 'manual', 'deploymentId' => $deployment->isEmpty() ? '' : $deployment->getId(), - 'deploymentInternalId' => $deployment->isEmpty() ? '' : $deployment->getInternalId(), + 'deploymentInternalId' => $deployment->isEmpty() ? '' : $deployment->getSequence(), 'deploymentResourceType' => 'site', 'deploymentResourceId' => $site->getId(), - 'deploymentResourceInternalId' => $site->getInternalId(), + 'deploymentResourceInternalId' => $site->getSequence(), 'deploymentVcsProviderBranch' => $branch, 'certificateId' => '', 'search' => implode(' ', [$ruleId, $domain->get(), $branch]), @@ -192,7 +192,7 @@ class Create extends Action $queueForCertificates ->setDomain(new Document([ 'domain' => $rule->getAttribute('domain'), - 'domainType' => 'site', + 'domainType' => $rule->getAttribute('deploymentResourceType', $rule->getAttribute('type')), ])) ->trigger(); } diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php index 7400d86e74..62c5998c9b 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php @@ -85,6 +85,7 @@ class Create extends Action ->inject('deviceForSites') ->inject('deviceForLocal') ->inject('queueForBuilds') + ->inject('plan') ->callback([$this, 'action']); } @@ -103,7 +104,8 @@ class Create extends Action Event $queueForEvents, Device $deviceForSites, Device $deviceForLocal, - Build $queueForBuilds + Build $queueForBuilds, + array $plan ) { $activate = \strval($activate) === 'true' || \strval($activate) === '1'; @@ -136,8 +138,14 @@ class Create extends Action throw new Exception(Exception::STORAGE_FILE_EMPTY, 'No file sent'); } + $siteSizeLimit = (int) System::getEnv('_APP_COMPUTE_SIZE_LIMIT', '30000000'); + + if (isset($plan['deploymentSize'])) { + $siteSizeLimit = $plan['deploymentSize'] * 1000 * 1000; + } + $fileExt = new FileExt([FileExt::TYPE_GZIP]); - $fileSizeValidator = new FileSize(System::getEnv('_APP_COMPUTE_SIZE_LIMIT', '30000000')); + $fileSizeValidator = new FileSize($siteSizeLimit); $upload = new Upload(); // Make sure we handle a single file and multiple files the same way @@ -175,7 +183,7 @@ class Create extends Action } } - if (!$fileSizeValidator->isValid($fileSize)) { // Check if file size is exceeding allowed limit + if (!$fileSizeValidator->isValid($fileSize) && $siteSizeLimit !== 0) { // Check if file size is exceeding allowed limit throw new Exception(Exception::STORAGE_INVALID_FILE_SIZE); } @@ -238,7 +246,7 @@ class Create extends Action Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'resourceInternalId' => $site->getInternalId(), + 'resourceInternalId' => $site->getSequence(), 'resourceId' => $site->getId(), 'resourceType' => 'sites', 'buildCommands' => \implode(' && ', $commands), @@ -255,7 +263,7 @@ class Create extends Action $site = $site ->setAttribute('latestDeploymentId', $deployment->getId()) - ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getSequence()) ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('sites', $site->getId(), $site); @@ -270,15 +278,15 @@ class Create extends Action fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'domain' => $domain, 'type' => 'deployment', 'trigger' => 'deployment', 'deploymentId' => $deployment->isEmpty() ? '' : $deployment->getId(), - 'deploymentInternalId' => $deployment->isEmpty() ? '' : $deployment->getInternalId(), + 'deploymentInternalId' => $deployment->isEmpty() ? '' : $deployment->getSequence(), 'deploymentResourceType' => 'site', 'deploymentResourceId' => $site->getId(), - 'deploymentResourceInternalId' => $site->getInternalId(), + 'deploymentResourceInternalId' => $site->getSequence(), 'status' => 'verified', 'certificateId' => '', 'search' => implode(' ', [$ruleId, $domain]), @@ -304,7 +312,7 @@ class Create extends Action Permission::update(Role::any()), Permission::delete(Role::any()), ], - 'resourceInternalId' => $site->getInternalId(), + 'resourceInternalId' => $site->getSequence(), 'resourceId' => $site->getId(), 'resourceType' => 'sites', 'buildCommands' => \implode(' && ', $commands), @@ -323,7 +331,7 @@ class Create extends Action $site = $site ->setAttribute('latestDeploymentId', $deployment->getId()) - ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getSequence()) ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('sites', $site->getId(), $site); @@ -335,14 +343,20 @@ class Create extends Action fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'domain' => $domain, 'type' => 'deployment', 'trigger' => 'deployment', - 'value' => $deployment->getId(), + 'deploymentId' => $deployment->isEmpty() ? '' : $deployment->getId(), + 'deploymentInternalId' => $deployment->isEmpty() ? '' : $deployment->getSequence(), + 'deploymentResourceType' => 'site', + 'deploymentResourceId' => $site->getId(), + 'deploymentResourceInternalId' => $site->getSequence(), 'status' => 'verified', 'certificateId' => '', 'search' => implode(' ', [$ruleId, $domain]), + 'owner' => 'Appwrite', + 'region' => $project->getAttribute('region') ])) ); } else { diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Delete.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Delete.php index 27db10c484..8717d04fc2 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Delete.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Delete.php @@ -101,7 +101,7 @@ class Delete extends Action if ($site->getAttribute('latestDeploymentId') === $deployment->getId()) { $latestDeployment = $dbForProject->findOne('deployments', [ Query::equal('resourceType', ['sites']), - Query::equal('resourceInternalId', [$site->getInternalId()]), + Query::equal('resourceInternalId', [$site->getSequence()]), Query::orderDesc('$createdAt'), ]); $site = $dbForProject->updateDocument( @@ -109,7 +109,7 @@ class Delete extends Action $site->getId(), $site ->setAttribute('latestDeploymentCreatedAt', $latestDeployment->isEmpty() ? '' : $latestDeployment->getCreatedAt()) - ->setAttribute('latestDeploymentInternalId', $latestDeployment->isEmpty() ? '' : $latestDeployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $latestDeployment->isEmpty() ? '' : $latestDeployment->getSequence()) ->setAttribute('latestDeploymentId', $latestDeployment->isEmpty() ? '' : $latestDeployment->getId()) ->setAttribute('latestDeploymentStatus', $latestDeployment->isEmpty() ? '' : $latestDeployment->getAttribute('status', '')) ); diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Download/Get.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Download/Get.php index e83af0bab6..d3a2f0b814 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Download/Get.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Download/Get.php @@ -99,12 +99,14 @@ class Get extends Action } if (!$device->exists($path)) { - throw new Exception(Exception::BUILD_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } $response ->setContentType('application/gzip') - ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache + ->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate') + ->addHeader('Expires', '0') + ->addHeader('Pragma', 'no-cache') ->addHeader('X-Peak', \memory_get_peak_usage()) ->addHeader('Content-Disposition', 'attachment; filename="' . $deploymentId . '-' . $type . '.tar.gz"'); diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php index bff0735328..739c701a2e 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Duplicate/Create.php @@ -110,9 +110,9 @@ class Create extends Action $commands[] = $site->getAttribute('buildCommand', ''); } - $deployment->removeAttribute('$internalId'); + $deployment->removeAttribute('$sequence'); $deployment = $dbForProject->createDocument('deployments', $deployment->setAttributes([ - '$internalId' => '', + '$sequence' => '', '$id' => $deploymentId, 'sourcePath' => $destination, 'totalSize' => $deployment->getAttribute('sourceSize', 0), @@ -134,7 +134,7 @@ class Create extends Action $site = $site ->setAttribute('latestDeploymentId', $deployment->getId()) - ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getSequence()) ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('sites', $site->getId(), $site); @@ -150,15 +150,15 @@ class Create extends Action fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'domain' => $domain, 'type' => 'deployment', 'trigger' => 'deployment', 'deploymentId' => $deployment->isEmpty() ? '' : $deployment->getId(), - 'deploymentInternalId' => $deployment->isEmpty() ? '' : $deployment->getInternalId(), + 'deploymentInternalId' => $deployment->isEmpty() ? '' : $deployment->getSequence(), 'deploymentResourceType' => 'site', 'deploymentResourceId' => $site->getId(), - 'deploymentResourceInternalId' => $site->getInternalId(), + 'deploymentResourceInternalId' => $site->getSequence(), 'status' => 'verified', 'certificateId' => '', 'owner' => 'Appwrite', diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Status/Update.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Status/Update.php index 2c6da43893..046ddd1ac8 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Status/Update.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Status/Update.php @@ -95,7 +95,7 @@ class Update extends Action 'status' => 'canceled' ])); - if ($deployment->getInternalId() === $site->getAttribute('latestDeploymentInternalId', '')) { + if ($deployment->getSequence() === $site->getAttribute('latestDeploymentInternalId', '')) { $site = $site->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('sites', $site->getId(), $site); } diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php index 5b29521ce5..5de688fbf8 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php @@ -153,7 +153,7 @@ class Create extends Base Permission::delete(Role::any()), ], 'resourceId' => $site->getId(), - 'resourceInternalId' => $site->getInternalId(), + 'resourceInternalId' => $site->getSequence(), 'resourceType' => 'sites', 'buildCommands' => \implode(' && ', $commands), 'buildOutput' => $site->getAttribute('outputDirectory', ''), @@ -165,7 +165,7 @@ class Create extends Base $site = $site ->setAttribute('latestDeploymentId', $deployment->getId()) - ->setAttribute('latestDeploymentInternalId', $deployment->getInternalId()) + ->setAttribute('latestDeploymentInternalId', $deployment->getSequence()) ->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt()) ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('sites', $site->getId(), $site); @@ -180,15 +180,15 @@ class Create extends Base fn () => $dbForPlatform->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'domain' => $domain, 'type' => 'deployment', 'trigger' => 'deployment', 'deploymentId' => $deployment->isEmpty() ? '' : $deployment->getId(), - 'deploymentInternalId' => $deployment->isEmpty() ? '' : $deployment->getInternalId(), + 'deploymentInternalId' => $deployment->isEmpty() ? '' : $deployment->getSequence(), 'deploymentResourceType' => 'site', 'deploymentResourceId' => $site->getId(), - 'deploymentResourceInternalId' => $site->getInternalId(), + 'deploymentResourceInternalId' => $site->getSequence(), 'status' => 'verified', 'certificateId' => '', 'owner' => 'Appwrite', diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/XList.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/XList.php index d7cfc1d3ad..306f756d87 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/XList.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/XList.php @@ -79,7 +79,7 @@ class XList extends Action } // Set resource queries - $queries[] = Query::equal('resourceInternalId', [$site->getInternalId()]); + $queries[] = Query::equal('resourceInternalId', [$site->getSequence()]); $queries[] = Query::equal('resourceType', ['sites']); /** diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Logs/Delete.php b/src/Appwrite/Platform/Modules/Sites/Http/Logs/Delete.php index a411cee91f..2d87321086 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Logs/Delete.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Logs/Delete.php @@ -71,7 +71,7 @@ class Delete extends Base throw new Exception(Exception::LOG_NOT_FOUND); } - if ($log->getAttribute('resourceType') !== 'sites' && $log->getAttribute('resourceInternalId') !== $site->getInternalId()) { + if ($log->getAttribute('resourceType') !== 'sites' && $log->getAttribute('resourceInternalId') !== $site->getSequence()) { throw new Exception(Exception::LOG_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Logs/Get.php b/src/Appwrite/Platform/Modules/Sites/Http/Logs/Get.php index 3d1ace2d20..2ef7d75539 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Logs/Get.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Logs/Get.php @@ -63,7 +63,7 @@ class Get extends Base $log = $dbForProject->getDocument('executions', $logId); - if ($log->getAttribute('resourceType') !== 'sites' && $log->getAttribute('resourceInternalId') !== $site->getInternalId()) { + if ($log->getAttribute('resourceType') !== 'sites' && $log->getAttribute('resourceInternalId') !== $site->getSequence()) { throw new Exception(Exception::LOG_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Logs/XList.php b/src/Appwrite/Platform/Modules/Sites/Http/Logs/XList.php index a873d32603..7e2c587797 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Logs/XList.php @@ -75,7 +75,7 @@ class XList extends Base } // Set internal queries - $queries[] = Query::equal('resourceInternalId', [$site->getInternalId()]); + $queries[] = Query::equal('resourceInternalId', [$site->getSequence()]); $queries[] = Query::equal('resourceType', ['sites']); /** diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php index c509799024..6dd1865047 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php @@ -154,7 +154,7 @@ class Create extends Base 'search' => implode(' ', [$siteId, $name, $framework]), 'fallbackFile' => $fallbackFile, 'installationId' => $installation->getId(), - 'installationInternalId' => $installation->getInternalId(), + 'installationInternalId' => $installation->getSequence(), 'providerRepositoryId' => $providerRepositoryId, 'repositoryId' => '', 'repositoryInternalId' => '', @@ -180,18 +180,18 @@ class Create extends Base Permission::delete(Role::team(ID::custom($teamId), 'developer')), ], 'installationId' => $installation->getId(), - 'installationInternalId' => $installation->getInternalId(), + 'installationInternalId' => $installation->getSequence(), 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'providerRepositoryId' => $providerRepositoryId, 'resourceId' => $site->getId(), - 'resourceInternalId' => $site->getInternalId(), + 'resourceInternalId' => $site->getSequence(), 'resourceType' => 'site', 'providerPullRequestIds' => [] ])); $site->setAttribute('repositoryId', $repository->getId()); - $site->setAttribute('repositoryInternalId', $repository->getInternalId()); + $site->setAttribute('repositoryInternalId', $repository->getSequence()); } $site = $dbForProject->updateDocument('sites', $site->getId(), $site); diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Deployment/Update.php b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Deployment/Update.php index 7f1681c0f1..a45c2a1243 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Deployment/Update.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Deployment/Update.php @@ -5,13 +5,14 @@ namespace Appwrite\Platform\Modules\Sites\Http\Sites\Deployment; use Appwrite\Event\Event; use Appwrite\Extend\Exception; use Appwrite\Platform\Modules\Compute\Base; -use Appwrite\Query; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -86,10 +87,8 @@ class Update extends Base throw new Exception(Exception::BUILD_NOT_READY); } - $oldDeploymentInternalId = $site->getAttribute('deploymentInternalId', ''); - $site = $dbForProject->updateDocument('sites', $site->getId(), new Document(array_merge($site->getArrayCopy(), [ - 'deploymentInternalId' => $deployment->getInternalId(), + 'deploymentInternalId' => $deployment->getSequence(), 'deploymentId' => $deployment->getId(), 'deploymentScreenshotDark' => $deployment->getAttribute('screenshotDark', ''), 'deploymentScreenshotLight' => $deployment->getAttribute('screenshotLight', ''), @@ -97,24 +96,21 @@ class Update extends Base ]))); $queries = [ - Query::equal('trigger', 'manual'), - Query::equal("type", ["deployment"]), - Query::equal("deploymentResourceType", ["site"]), - Query::equal("deploymentResourceInternalId", [$site->getInternalId()]), + Query::equal('trigger', ['manual']), + Query::equal('type', ['deployment']), + Query::equal('deploymentResourceType', ['site']), + Query::equal('deploymentResourceInternalId', [$site->getSequence()]), + Query::equal('deploymentVcsProviderBranch', ['']), + Query::equal('projectInternalId', [$project->getSequence()]) ]; - if (empty($oldDeploymentInternalId)) { - $queries[] = Query::equal("deploymentInternalId", [""]); - } else { - $queries[] = Query::equal("deploymentInternalId", [$oldDeploymentInternalId]); - } - - $this->listRules($project, $queries, $dbForPlatform, function (Document $rule) use ($dbForPlatform, $deployment) { + Authorization::skip(fn () => $dbForPlatform->foreach('rules', function (Document $rule) use ($dbForPlatform, $deployment) { $rule = $rule ->setAttribute('deploymentId', $deployment->getId()) - ->setAttribute('deploymentInternalId', $deployment->getInternalId()); - $dbForPlatform->updateDocument('rules', $rule->getId(), $rule); - }); + ->setAttribute('deploymentInternalId', $deployment->getSequence()); + + Authorization::skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), $rule)); + }, $queries)); $queueForEvents ->setParam('siteId', $site->getId()) diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php index 212b410178..85f1ee8845 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php @@ -173,8 +173,8 @@ class Update extends Base // Git disconnect logic. Disconnecting only when providerRepositoryId is empty, allowing for continue updates without disconnecting git if ($isConnected && ($providerRepositoryId !== null && empty($providerRepositoryId))) { $repositories = $dbForPlatform->find('repositories', [ - Query::equal('projectInternalId', [$project->getInternalId()]), - Query::equal('resourceInternalId', [$site->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), + Query::equal('resourceInternalId', [$site->getSequence()]), Query::equal('resourceType', ['site']), Query::limit(100), ]); @@ -206,18 +206,18 @@ class Update extends Base Permission::delete(Role::team(ID::custom($teamId), 'developer')), ], 'installationId' => $installation->getId(), - 'installationInternalId' => $installation->getInternalId(), + 'installationInternalId' => $installation->getSequence(), 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), + 'projectInternalId' => $project->getSequence(), 'providerRepositoryId' => $providerRepositoryId, 'resourceId' => $site->getId(), - 'resourceInternalId' => $site->getInternalId(), + 'resourceInternalId' => $site->getSequence(), 'resourceType' => 'site', 'providerPullRequestIds' => [] ])); $repositoryId = $repository->getId(); - $repositoryInternalId = $repository->getInternalId(); + $repositoryInternalId = $repository->getSequence(); } $live = true; @@ -256,7 +256,7 @@ class Update extends Base 'buildCommand' => $buildCommand, 'outputDirectory' => $outputDirectory, 'installationId' => $installation->getId(), - 'installationInternalId' => $installation->getInternalId(), + 'installationInternalId' => $installation->getSequence(), 'providerRepositoryId' => $providerRepositoryId, 'repositoryId' => $repositoryId, 'repositoryInternalId' => $repositoryInternalId, diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php b/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php index 8bf19a9c28..f8df836085 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Usage/Get.php @@ -74,20 +74,20 @@ class Get extends Base $stats = $usage = []; $days = $periods[$range]; $metrics = [ - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_SUCCESS), - str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_FAILED), - str_replace(['{siteInternalId}'], [$site->getInternalId()], METRIC_SITES_ID_REQUESTS), - str_replace(['{siteInternalId}'], [$site->getInternalId()], METRIC_SITES_ID_INBOUND), - str_replace(['{siteInternalId}'], [$site->getInternalId()], METRIC_SITES_ID_OUTBOUND), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_SUCCESS), + str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_SITES, $site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_FAILED), + str_replace(['{siteInternalId}'], [$site->getSequence()], METRIC_SITES_ID_REQUESTS), + str_replace(['{siteInternalId}'], [$site->getSequence()], METRIC_SITES_ID_INBOUND), + str_replace(['{siteInternalId}'], [$site->getSequence()], METRIC_SITES_ID_OUTBOUND), ]; diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Variables/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Variables/Create.php index ff1a5c50ec..88da635fe4 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Variables/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Variables/Create.php @@ -85,7 +85,7 @@ class Create extends Base Permission::delete(Role::team(ID::custom($teamId), 'owner')), Permission::delete(Role::team(ID::custom($teamId), 'developer')), ], - 'resourceInternalId' => $site->getInternalId(), + 'resourceInternalId' => $site->getSequence(), 'resourceId' => $site->getId(), 'resourceType' => 'site', 'key' => $key, diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Variables/Delete.php b/src/Appwrite/Platform/Modules/Sites/Http/Variables/Delete.php index e4594a7c3d..ea927be367 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Variables/Delete.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Variables/Delete.php @@ -66,7 +66,7 @@ class Delete extends Base } $variable = $dbForProject->getDocument('variables', $variableId); - if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $site->getInternalId() || $variable->getAttribute('resourceType') !== 'site') { + if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $site->getSequence() || $variable->getAttribute('resourceType') !== 'site') { throw new Exception(Exception::VARIABLE_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Variables/Get.php b/src/Appwrite/Platform/Modules/Sites/Http/Variables/Get.php index a14129d432..1c25f9e701 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Variables/Get.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Variables/Get.php @@ -68,7 +68,7 @@ class Get extends Base if ( $variable === false || $variable->isEmpty() || - $variable->getAttribute('resourceInternalId') !== $site->getInternalId() || + $variable->getAttribute('resourceInternalId') !== $site->getSequence() || $variable->getAttribute('resourceType') !== 'site' ) { throw new Exception(Exception::VARIABLE_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Variables/Update.php b/src/Appwrite/Platform/Modules/Sites/Http/Variables/Update.php index cd438a0ebb..698992f79f 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Variables/Update.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Variables/Update.php @@ -77,7 +77,7 @@ class Update extends Base } $variable = $dbForProject->getDocument('variables', $variableId); - if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $site->getInternalId() || $variable->getAttribute('resourceType') !== 'site') { + if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceInternalId') !== $site->getSequence() || $variable->getAttribute('resourceType') !== 'site') { throw new Exception(Exception::VARIABLE_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Action.php b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Action.php index 565ab7bab7..bcefaf353f 100644 --- a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Action.php +++ b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Action.php @@ -29,9 +29,9 @@ class Action extends UtopiaAction $fileSecurity = $bucket->getAttribute('fileSecurity', false); if ($fileSecurity) { - $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); } else { - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); } if ($file->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php index 4b3b421696..dc1e5232b6 100644 --- a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php +++ b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php @@ -48,7 +48,7 @@ class Create extends Action group: 'files', name: 'createFileToken', description: << ID::unique(), 'secret' => Auth::tokenGenerator(128), 'resourceId' => $bucketId . ':' . $fileId, - 'resourceInternalId' => $bucket->getInternalId() . ':' . $file->getInternalId(), + 'resourceInternalId' => $bucket->getSequence() . ':' . $file->getSequence(), 'resourceType' => TOKENS_RESOURCE_TYPE_FILES, 'expire' => $expire, ])); diff --git a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/XList.php b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/XList.php index 38fe10e2d9..628d9b768f 100644 --- a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/XList.php +++ b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/XList.php @@ -64,7 +64,7 @@ class XList extends Action $queries = Query::parseQueries($queries); $queries[] = Query::equal('resourceType', [TOKENS_RESOURCE_TYPE_FILES]); - $queries[] = Query::equal('resourceInternalId', [$bucket->getInternalId() . ':' . $file->getInternalId()]); + $queries[] = Query::equal('resourceInternalId', [$bucket->getSequence() . ':' . $file->getSequence()]); // Get cursor document if there was a cursor query $cursor = \array_filter($queries, function ($query) { return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); diff --git a/src/Appwrite/Platform/Tasks/Doctor.php b/src/Appwrite/Platform/Tasks/Doctor.php index 790f1a7290..b543555477 100644 --- a/src/Appwrite/Platform/Tasks/Doctor.php +++ b/src/Appwrite/Platform/Tasks/Doctor.php @@ -3,14 +3,19 @@ namespace Appwrite\Platform\Tasks; use Appwrite\ClamAV\Network; -use Appwrite\PubSub\Adapter; +use Appwrite\PubSub\Adapter\Pool as PubSubPool; +use PHPMailer\PHPMailer\PHPMailer; use Utopia\App; +use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\CLI\Console; use Utopia\Config\Config; +use Utopia\Database\Adapter\Pool as DatabasePool; use Utopia\Domains\Domain; use Utopia\DSN\DSN; use Utopia\Logger\Logger; use Utopia\Platform\Action; +use Utopia\Pools\Group; +use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\Registry\Registry; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; @@ -89,9 +94,9 @@ class Doctor extends Action Console::log('🟢 Abuse protection is enabled'); } - $authWhitelistRoot = System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', null); - $authWhitelistEmails = System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null); - $authWhitelistIPs = System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null); + $authWhitelistRoot = System::getEnv('_APP_CONSOLE_WHITELIST_ROOT'); + $authWhitelistEmails = System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS'); + $authWhitelistIPs = System::getEnv('_APP_CONSOLE_WHITELIST_IPS'); if ( empty($authWhitelistRoot) @@ -127,19 +132,16 @@ class Doctor extends Action } else { Console::log('🟢 Logging adapter is enabled (' . $providerName . ')'); } - } catch (\Throwable $th) { + } catch (\Throwable) { Console::log('🔴 Logging adapter is misconfigured'); } \usleep(200 * 1000); // Sleep for 0.2 seconds - try { - Console::log("\n" . '[Connectivity]'); - } catch (\Throwable $th) { - //throw $th; - } + Console::log("\n" . '[Connectivity]'); - $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ + /** @var Group $pools */ + $pools = $register->get('pools'); $configs = [ 'Console.DB' => Config::getParam('pools-console'), @@ -149,20 +151,22 @@ class Doctor extends Action foreach ($configs as $key => $config) { foreach ($config as $database) { try { - $adapter = $pools->get($database)->pop()->getResource(); + $adapter = new DatabasePool($pools->get($database)); if ($adapter->ping()) { Console::success('🟢 ' . str_pad("{$key}({$database})", 50, '.') . 'connected'); } else { Console::error('🔴 ' . str_pad("{$key}({$database})", 47, '.') . 'disconnected'); } - } catch (\Throwable $th) { + } catch (\Throwable) { Console::error('🔴 ' . str_pad("{$key}.({$database})", 47, '.') . 'disconnected'); } } } - $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ + /** @var Group $pools */ + $pools = $register->get('pools'); + $configs = [ 'Cache' => Config::getParam('pools-cache'), 'Queue' => Config::getParam('pools-queue'), @@ -172,15 +176,18 @@ class Doctor extends Action foreach ($configs as $key => $config) { foreach ($config as $pool) { try { - /** @var Adapter $adapter */ - $adapter = $pools->get($pool)->pop()->getResource(); + $adapter = match($key) { + 'Cache' => new CachePool($pools->get($pool)), + 'Queue' => new BrokerPool($pools->get($pool)), + 'PubSub' => new PubSubPool($pools->get($pool)), + }; if ($adapter->ping()) { Console::success('🟢 ' . str_pad("{$key}({$pool})", 50, '.') . 'connected'); } else { Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected'); } - } catch (\Throwable $th) { + } catch (\Throwable) { Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected'); } } @@ -198,13 +205,14 @@ class Doctor extends Action } else { Console::error('🔴 ' . str_pad("Antivirus", 47, '.') . 'disconnected'); } - } catch (\Throwable $th) { + } catch (\Throwable) { Console::error('🔴 ' . str_pad("Antivirus", 47, '.') . 'disconnected'); } } try { - $mail = $register->get('smtp'); /* @var $mail \PHPMailer\PHPMailer\PHPMailer */ + /* @var PHPMailer $mail */ + $mail = $register->get('smtp'); $mail->addAddress('demo@example.com', 'Example.com'); $mail->Subject = 'Test SMTP Connection'; @@ -213,7 +221,7 @@ class Doctor extends Action $mail->send(); Console::success('🟢 ' . str_pad("SMTP", 50, '.') . 'connected'); - } catch (\Throwable $th) { + } catch (\Throwable) { Console::error('🔴 ' . str_pad("SMTP", 47, '.') . 'disconnected'); } @@ -287,7 +295,7 @@ class Doctor extends Action Console::error('Failed to check for a newer version' . "\n"); } } - } catch (\Throwable $th) { + } catch (\Throwable) { Console::error('Failed to check for a newer version' . "\n"); } } diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index fd9d05dec9..ddc8c3cb6f 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -4,9 +4,11 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Certificate; use Appwrite\Event\Delete; +use DateInterval; +use DateTime; use Utopia\CLI\Console; use Utopia\Database\Database; -use Utopia\Database\DateTime; +use Utopia\Database\DateTime as DatabaseDateTime; use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Platform\Action; @@ -58,29 +60,35 @@ class Maintenance extends Action Console::info('Setting loop start time to ' . $next->format("Y-m-d H:i:s.v") . '. Delaying for ' . $delay . ' seconds.'); Console::loop(function () use ($interval, $cacheRetention, $schedulesDeletionRetention, $usageStatsRetentionHourly, $dbForPlatform, $console, $queueForDeletes, $queueForCertificates) { - $time = DateTime::now(); + $time = DatabaseDateTime::now(); Console::info("[{$time}] Notifying workers with maintenance tasks every {$interval} seconds"); + // Iterate through project only if it was accessed in last 30 days + $dateInterval = DateInterval::createFromDateString('30 days'); + $before30days = (new DateTime())->sub($dateInterval); + $dbForPlatform->foreach( 'projects', function (Document $project) use ($queueForDeletes, $usageStatsRetentionHourly) { $queueForDeletes ->setType(DELETE_TYPE_MAINTENANCE) ->setProject($project) - ->setUsageRetentionHourlyDateTime(DateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly)) + ->setUsageRetentionHourlyDateTime(DatabaseDateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly)) ->trigger(); }, [ Query::equal('region', [System::getEnv('_APP_REGION', 'default')]), Query::limit(100), + Query::greaterThanEqual('accessedAt', DatabaseDateTime::format($before30days)), + Query::orderAsc('teamInternalId'), ] ); $queueForDeletes ->setType(DELETE_TYPE_MAINTENANCE) ->setProject($console) - ->setUsageRetentionHourlyDateTime(DateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly)) + ->setUsageRetentionHourlyDateTime(DatabaseDateTime::addSeconds(new \DateTime(), -1 * $usageStatsRetentionHourly)) ->trigger(); $this->notifyDeleteConnections($queueForDeletes); @@ -94,13 +102,13 @@ class Maintenance extends Action { $queueForDeletes ->setType(DELETE_TYPE_REALTIME) - ->setDatetime(DateTime::addSeconds(new \DateTime(), -60)) + ->setDatetime(DatabaseDateTime::addSeconds(new \DateTime(), -60)) ->trigger(); } private function renewCertificates(Database $dbForPlatform, Certificate $queueForCertificate): void { - $time = DateTime::now(); + $time = DatabaseDateTime::now(); $certificates = $dbForPlatform->find('certificates', [ Query::lessThan('attempts', 5), // Maximum 5 attempts @@ -129,7 +137,7 @@ class Maintenance extends Action { $queueForDeletes ->setType(DELETE_TYPE_CACHE_BY_TIMESTAMP) - ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) + ->setDatetime(DatabaseDateTime::addSeconds(new \DateTime(), -1 * $interval)) ->trigger(); } @@ -137,7 +145,7 @@ class Maintenance extends Action { $queueForDeletes ->setType(DELETE_TYPE_SCHEDULES) - ->setDatetime(DateTime::addSeconds(new \DateTime(), -1 * $interval)) + ->setDatetime(DatabaseDateTime::addSeconds(new \DateTime(), -1 * $interval)) ->trigger(); } } diff --git a/src/Appwrite/Platform/Tasks/SDKs.php b/src/Appwrite/Platform/Tasks/SDKs.php index cf917ae96b..07749400d6 100644 --- a/src/Appwrite/Platform/Tasks/SDKs.php +++ b/src/Appwrite/Platform/Tasks/SDKs.php @@ -64,7 +64,7 @@ class SDKs extends Action $message ??= Console::confirm('Please enter your commit message:'); } - if (!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x', '0.13.x', '0.14.x', '0.15.x', '1.0.x', '1.1.x', '1.2.x', '1.3.x', '1.4.x', '1.5.x', '1.6.x', '1.7.x', 'latest'])) { + if (!\in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x', '0.13.x', '0.14.x', '0.15.x', '1.0.x', '1.1.x', '1.2.x', '1.3.x', '1.4.x', '1.5.x', '1.6.x', '1.7.x', '1.8.x', 'latest'])) { throw new \Exception('Unknown version given'); } @@ -251,7 +251,8 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ->setDiscord(APP_SOCIAL_DISCORD_CHANNEL, APP_SOCIAL_DISCORD) ->setDefaultHeaders([ 'X-Appwrite-Response-Format' => '1.7.0', - ]); + ]) + ->setExclude($language['exclude'] ?? []); // Make sure we have a clean slate. // Otherwise, all files in this dir will be pushed, @@ -312,7 +313,5 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND } } } - - Console::exit(); } } diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index 3cf89f9f44..b2134707e7 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -12,6 +12,7 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Platform\Action; use Utopia\Pools\Group; +use Utopia\Queue\Broker\Pool as BrokerPool; use Utopia\System\System; use Utopia\Telemetry\Adapter as Telemetry; use Utopia\Telemetry\Gauge; @@ -24,6 +25,8 @@ abstract class ScheduleBase extends Action protected array $schedules = []; + protected BrokerPool $publisher; + private ?Histogram $collectSchedulesTelemetryDuration = null; private ?Gauge $collectSchedulesTelemetryCount = null; private ?Gauge $scheduleTelemetryCount = null; @@ -68,6 +71,7 @@ abstract class ScheduleBase extends Action Console::title(\ucfirst(static::getSupportedResource()) . ' scheduler V1'); Console::success(APP_NAME . ' ' . \ucfirst(static::getSupportedResource()) . ' scheduler v1 has started'); + $this->publisher = new BrokerPool($pools->get('publisher')); $this->scheduleTelemetryCount = $telemetry->createGauge('task.schedule.count'); $this->collectSchedulesTelemetryDuration = $telemetry->createHistogram('task.schedule.collect_schedules.duration', 's'); $this->collectSchedulesTelemetryCount = $telemetry->createGauge('task.schedule.collect_schedules.count'); @@ -75,16 +79,16 @@ abstract class ScheduleBase extends Action // start with "0" to load all active documents. $lastSyncUpdate = "0"; - $this->collectSchedules($pools, $dbForPlatform, $getProjectDB, $lastSyncUpdate); + $this->collectSchedules($dbForPlatform, $getProjectDB, $lastSyncUpdate); Console::success("Starting timers at " . DateTime::now()); /** * The timer synchronize $schedules copy with database collection. */ - Timer::tick(static::UPDATE_TIMER * 1000, function () use ($pools, $dbForPlatform, $getProjectDB, &$lastSyncUpdate) { + Timer::tick(static::UPDATE_TIMER * 1000, function () use ($dbForPlatform, $getProjectDB, &$lastSyncUpdate) { $time = DateTime::now(); Console::log("Sync tick: Running at $time"); - $this->collectSchedules($pools, $dbForPlatform, $getProjectDB, $lastSyncUpdate); + $this->collectSchedules($dbForPlatform, $getProjectDB, $lastSyncUpdate); }); while (true) { @@ -99,7 +103,7 @@ abstract class ScheduleBase extends Action } } - private function collectSchedules(Group $pools, Database $dbForPlatform, callable $getProjectDB, string &$lastSyncUpdate): void + private function collectSchedules(Database $dbForPlatform, callable $getProjectDB, string &$lastSyncUpdate): void { // If we haven't synced yet, load all active schedules $initialLoad = $lastSyncUpdate === "0"; @@ -111,7 +115,7 @@ abstract class ScheduleBase extends Action * @throws Exception * @var Document $schedule */ - $getSchedule = function (Document $schedule) use ($pools, $dbForPlatform, $getProjectDB): array { + $getSchedule = function (Document $schedule) use ($dbForPlatform, $getProjectDB): array { $project = $dbForPlatform->getDocument('projects', $schedule->getAttribute('projectId')); $resource = $getProjectDB($project)->getDocument( @@ -119,10 +123,8 @@ abstract class ScheduleBase extends Action $schedule->getAttribute('resourceId') ); - $pools->reclaim(); - return [ - '$internalId' => $schedule->getInternalId(), + '$sequence' => $schedule->getSequence(), '$id' => $schedule->getId(), 'resourceId' => $schedule->getAttribute('resourceId'), 'schedule' => $schedule->getAttribute('schedule'), @@ -173,19 +175,19 @@ abstract class ScheduleBase extends Action $total = $total + $sum; foreach ($results as $document) { - $localDocument = $this->schedules[$document->getInternalId()] ?? null; + $localDocument = $this->schedules[$document->getSequence()] ?? null; if ($localDocument !== null) { if (!$document['active']) { Console::info("Removing: {$document['resourceType']}::{$document['resourceId']}"); - unset($this->schedules[$document->getInternalId()]); + unset($this->schedules[$document->getSequence()]); } elseif (strtotime($localDocument['resourceUpdatedAt']) !== strtotime($document['resourceUpdatedAt'])) { Console::info("Updating: {$document['resourceType']}::{$document['resourceId']}"); - $this->schedules[$document->getInternalId()] = $getSchedule($document); + $this->schedules[$document->getSequence()] = $getSchedule($document); } } else { try { - $this->schedules[$document->getInternalId()] = $getSchedule($document); + $this->schedules[$document->getSequence()] = $getSchedule($document); } catch (\Throwable $th) { $collectionId = static::getCollectionId(); Console::error("Failed to load schedule for project {$document['projectId']} {$collectionId} {$document['resourceId']}"); diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index 2a8fd99b7f..27a7c1dbf1 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -29,9 +29,7 @@ class ScheduleExecutions extends ScheduleBase protected function enqueueResources(Group $pools, Database $dbForPlatform, callable $getProjectDB): void { - $queue = $pools->get('publisher')->pop(); - $connection = $queue->getResource(); - $queueForFunctions = new Func($connection); + $queueForFunctions = new Func($this->publisher); $intervalEnd = (new \DateTime())->modify('+' . self::ENQUEUE_TIMER . ' seconds'); foreach ($this->schedules as $schedule) { @@ -41,7 +39,7 @@ class ScheduleExecutions extends ScheduleBase $schedule['$id'], ); - unset($this->schedules[$schedule['$internalId']]); + unset($this->schedules[$schedule['$sequence']]); continue; } @@ -83,9 +81,7 @@ class ScheduleExecutions extends ScheduleBase $schedule['$id'], ); - unset($this->schedules[$schedule['$internalId']]); + unset($this->schedules[$schedule['$sequence']]); } - - $queue->reclaim(); } } diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index 6788748f3d..7812b27832 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -76,12 +76,9 @@ class ScheduleFunctions extends ScheduleBase } foreach ($delayedExecutions as $delay => $schedules) { - \go(function () use ($delay, $schedules, $pools, $dbForPlatform) { + \go(function () use ($delay, $schedules, $dbForPlatform) { \sleep($delay); // in seconds - $queue = $pools->get('publisher')->pop(); - $connection = $queue->getResource(); - foreach ($schedules as $delayConfig) { $scheduleKey = $delayConfig['key']; // Ensure schedule was not deleted @@ -93,7 +90,7 @@ class ScheduleFunctions extends ScheduleBase $this->updateProjectAccess($schedule['project'], $dbForPlatform); - $queueForFunctions = new Func($connection); + $queueForFunctions = new Func($this->publisher); $queueForFunctions ->setType('schedule') @@ -105,8 +102,6 @@ class ScheduleFunctions extends ScheduleBase $this->recordEnqueueDelay($delayConfig['nextDate']); } - - $queue->reclaim(); }); } diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index a15df6ed5b..af15f9583f 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -40,10 +40,8 @@ class ScheduleMessages extends ScheduleBase continue; } - \go(function () use ($schedule, $scheduledAt, $pools, $dbForPlatform) { - $queue = $pools->get('publisher')->pop(); - $connection = $queue->getResource(); - $queueForMessaging = new Messaging($connection); + \go(function () use ($schedule, $scheduledAt, $dbForPlatform) { + $queueForMessaging = new Messaging($this->publisher); $this->updateProjectAccess($schedule['project'], $dbForPlatform); @@ -58,9 +56,8 @@ class ScheduleMessages extends ScheduleBase $schedule['$id'], ); - $queue->reclaim(); $this->recordEnqueueDelay($scheduledAt); - unset($this->schedules[$schedule['$internalId']]); + unset($this->schedules[$schedule['$sequence']]); }); } } diff --git a/src/Appwrite/Platform/Tasks/Specs.php b/src/Appwrite/Platform/Tasks/Specs.php index 76c9db079a..4876e8d96c 100644 --- a/src/Appwrite/Platform/Tasks/Specs.php +++ b/src/Appwrite/Platform/Tasks/Specs.php @@ -101,6 +101,12 @@ class Specs extends Action 'name' => 'X-Appwrite-Session', 'description' => 'The user session to authenticate with', 'in' => 'header', + ], + 'DevKey' => [ + 'type' => 'apiKey', + 'name' => 'X-Appwrite-Dev-Key', + 'description' => 'Your secret dev API key', + 'in' => 'header', ] ], APP_PLATFORM_SERVER => [ diff --git a/src/Appwrite/Platform/Tasks/StatsResources.php b/src/Appwrite/Platform/Tasks/StatsResources.php index ca2a6860ff..a0b5056b0f 100644 --- a/src/Appwrite/Platform/Tasks/StatsResources.php +++ b/src/Appwrite/Platform/Tasks/StatsResources.php @@ -73,7 +73,7 @@ class StatsResources extends Action $queue ->setProject($project) ->trigger(); - Console::success('project: ' . $project->getId() . '(' . $project->getInternalId() . ')' . ' queued'); + Console::success('project: ' . $project->getId() . '(' . $project->getSequence() . ')' . ' queued'); }); }, $interval); diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 76309145b8..9d4bbbc408 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -12,13 +12,13 @@ use Utopia\Database\Exception\Authorization; use Utopia\Database\Exception\Structure; use Utopia\Platform\Action; use Utopia\Queue\Message; +use Utopia\Queue\Result\Commit; +use Utopia\Queue\Result\NoCommit; use Utopia\System\System; class Audits extends Action { - protected const BATCH_SIZE_DEVELOPMENT = 1; // smaller batch size for development - protected const BATCH_SIZE_PRODUCTION = 5_000; - protected const BATCH_AGGREGATION_INTERVAL = 60; // in seconds + protected const int BATCH_AGGREGATION_INTERVAL = 60; // in seconds private int $lastTriggeredTime = 0; @@ -27,9 +27,7 @@ class Audits extends Action protected function getBatchSize(): int { - return System::getEnv('_APP_ENV', 'development') === 'development' - ? self::BATCH_SIZE_DEVELOPMENT - : self::BATCH_SIZE_PRODUCTION; + return intval(System::getEnv('_APP_QUEUE_PREFETCH_COUNT', 1)); } public static function getName(): string @@ -57,13 +55,13 @@ class Audits extends Action * @param Message $message * @param callable $getProjectDB * @param Document $project - * @return void + * @return Commit|NoCommit * @throws Throwable * @throws \Utopia\Database\Exception * @throws Authorization * @throws Structure */ - public function action(Message $message, callable $getProjectDB, Document $project): void + public function action(Message $message, callable $getProjectDB, Document $project): Commit|NoCommit { $payload = $message->getPayload() ?? []; @@ -91,7 +89,7 @@ class Audits extends Action // Create event data $eventData = [ - 'userId' => $user->getInternalId(), + 'userId' => $user->getSequence(), 'event' => $event, 'resource' => $resource, 'userAgent' => $userAgent, @@ -108,13 +106,13 @@ class Audits extends Action 'timestamp' => date("Y-m-d H:i:s", $message->getTimestamp()), ]; - if (isset($this->logs[$project->getInternalId()])) { - $this->logs[$project->getInternalId()]['logs'][] = $eventData; + if (isset($this->logs[$project->getSequence()])) { + $this->logs[$project->getSequence()]['logs'][] = $eventData; } else { - $this->logs[$project->getInternalId()] = [ + $this->logs[$project->getSequence()] = [ 'project' => new Document([ '$id' => $project->getId(), - '$internalId' => $project->getInternalId(), + '$sequence' => $project->getSequence(), 'database' => $project->getAttribute('database'), ]), 'logs' => [$eventData] @@ -123,29 +121,32 @@ class Audits extends Action // Check if we should process the batch by checking both for the batch size and the elapsed time $batchSize = $this->getBatchSize(); - $shouldProcessBatch = \count($this->logs) >= $batchSize; - if (!$shouldProcessBatch && \count($this->logs) > 0) { + $logCount = array_reduce($this->logs, fn (int $current, $logs) => $current + count($logs['logs']), 0); + $shouldProcessBatch = $logCount >= $batchSize; + if (!$shouldProcessBatch && $logCount > 0) { $shouldProcessBatch = (\time() - $this->lastTriggeredTime) >= self::BATCH_AGGREGATION_INTERVAL; } - if ($shouldProcessBatch) { - try { - foreach ($this->logs as $internalId => $projectLogs) { - $dbForProject = $getProjectDB($projectLogs['project']); - - Console::log('Processing batch with ' . count($projectLogs['logs']) . ' events'); - $audit = new Audit($dbForProject); - - $audit->logBatch($projectLogs['logs']); - Console::success('Audit logs processed successfully'); - - unset($this->logs[$internalId]); - } - } catch (Throwable $e) { - Console::error('Error processing audit logs: ' . $e->getMessage()); - } finally { - $this->lastTriggeredTime = time(); - } + if (!$shouldProcessBatch) { + return new NoCommit(); } + + try { + foreach ($this->logs as $sequence => $projectLogs) { + $dbForProject = $getProjectDB($projectLogs['project']); + + Console::log('Processing batch with ' . count($projectLogs['logs']) . ' events'); + $audit = new Audit($dbForProject); + + $audit->logBatch($projectLogs['logs']); + Console::success('Audit logs processed successfully'); + + unset($this->logs[$sequence]); + } + } catch (Throwable $e) { + Console::error('Error processing audit logs: ' . $e->getMessage()); + } + $this->lastTriggeredTime = time(); + return new Commit(); } } diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index 4e83497cdd..a9104e0017 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -96,7 +96,7 @@ class Certificates extends Action $log->addTag('domain', $domain->get()); - $domainType = $payload['domainType'] ?? null; + $domainType = $document->getAttribute('domainType'); $this->execute($domain, $domainType, $dbForPlatform, $queueForMails, $queueForEvents, $queueForWebhooks, $queueForFunctions, $queueForRealtime, $log, $certificates, $skipRenewCheck, $plan); } @@ -255,7 +255,7 @@ class Certificates extends Action $certificate = new Document(\array_merge($certificateDocument->getArrayCopy(), $certificate->getArrayCopy())); $certificate = $dbForPlatform->updateDocument('certificates', $certificate->getId(), $certificate); } else { - $certificate->removeAttribute('$internalId'); + $certificate->removeAttribute('$sequence'); $certificate = $dbForPlatform->createDocument('certificates', $certificate); } diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index b2691b420e..8c6f6f0252 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -147,15 +147,15 @@ class Databases extends Action try { switch ($type) { case Database::VAR_RELATIONSHIP: - $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); + $relatedCollection = $dbForProject->getDocument('database_' . $database->getSequence(), $options['relatedCollection']); if ($relatedCollection->isEmpty()) { throw new DatabaseException('Collection not found'); } if ( !$dbForProject->createRelationship( - collection: 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), - relatedCollection: 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), + collection: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + relatedCollection: 'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(), type: $options['relationType'], twoWay: $options['twoWay'], id: $key, @@ -167,12 +167,12 @@ class Databases extends Action } if ($options['twoWay']) { - $relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); + $relatedAttribute = $dbForProject->getDocument('attributes', $database->getSequence() . '_' . $relatedCollection->getSequence() . '_' . $options['twoWayKey']); $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'available')); } break; default: - if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { + if (!$dbForProject->createAttribute('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) { throw new Exception('Failed to create Attribute'); } } @@ -207,10 +207,10 @@ class Databases extends Action $this->trigger($database, $collection, $project, $event, $queueForRealtime, $attribute); if (! $relatedCollection->isEmpty()) { - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $relatedCollection->getId()); } - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $collectionId); } } @@ -257,18 +257,18 @@ class Databases extends Action try { if ($type === Database::VAR_RELATIONSHIP) { if ($options['twoWay']) { - $relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']); + $relatedCollection = $dbForProject->getDocument('database_' . $database->getSequence(), $options['relatedCollection']); if ($relatedCollection->isEmpty()) { throw new DatabaseException('Collection not found'); } - $relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']); + $relatedAttribute = $dbForProject->getDocument('attributes', $database->getSequence() . '_' . $relatedCollection->getSequence() . '_' . $options['twoWayKey']); } - if (!$dbForProject->deleteRelationship('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { + if (!$dbForProject->deleteRelationship('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $key)) { $dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'stuck')); throw new DatabaseException('Failed to delete Relationship'); } - } elseif (!$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { + } elseif (!$dbForProject->deleteAttribute('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $key)) { throw new DatabaseException('Failed to delete Attribute'); } @@ -365,10 +365,10 @@ class Databases extends Action } } } finally { - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $collectionId); if (! $relatedCollection->isEmpty()) { - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId()); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $relatedCollection->getId()); } } } @@ -408,7 +408,7 @@ class Databases extends Action $project = $dbForPlatform->getDocument('projects', $projectId); try { - if (!$dbForProject->createIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) { + if (!$dbForProject->createIndex('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $key, $type, $attributes, $lengths, $orders)) { throw new DatabaseException('Failed to create Index'); } $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available')); @@ -426,7 +426,7 @@ class Databases extends Action throw $e; } finally { $this->trigger($database, $collection, $project, $event, $queueForRealtime, null, $index); - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collectionId); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $collectionId); } } @@ -461,7 +461,7 @@ class Databases extends Action $project = $dbForPlatform->getDocument('projects', $projectId); try { - if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) { + if ($status !== 'failed' && !$dbForProject->deleteIndex('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $key)) { throw new DatabaseException('Failed to delete index'); } $dbForProject->deleteDocument('indexes', $index->getId()); @@ -482,7 +482,7 @@ class Databases extends Action } finally { $this->trigger($database, $collection, $project, $event, $queueForRealtime, null, $index); - $dbForProject->purgeCachedDocument('database_' . $database->getInternalId(), $collection->getId()); + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $collection->getId()); } } @@ -495,11 +495,11 @@ class Databases extends Action */ protected function deleteDatabase(Document $database, Document $project, $dbForProject): void { - $this->deleteByGroup('database_' . $database->getInternalId(), [], $dbForProject, function ($collection) use ($database, $project, $dbForProject) { + $this->deleteByGroup('database_' . $database->getSequence(), [], $dbForProject, function ($collection) use ($database, $project, $dbForProject) { $this->deleteCollection($database, $collection, $project, $dbForProject); }); - $dbForProject->deleteCollection('database_' . $database->getInternalId()); + $dbForProject->deleteCollection('database_' . $database->getSequence()); } /** @@ -522,10 +522,10 @@ class Databases extends Action } $collectionId = $collection->getId(); - $collectionInternalId = $collection->getInternalId(); - $databaseInternalId = $database->getInternalId(); + $collectionInternalId = $collection->getSequence(); + $databaseInternalId = $database->getSequence(); - $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $collection->getInternalId()); + $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $collection->getSequence()); /** * Related collections relating to current collection diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 12143b4106..8ed96c76a2 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -34,7 +34,7 @@ use Utopia\System\System; class Deletes extends Action { - protected array $selects = ['$internalId', '$id', '$collection', '$permissions', '$updatedAt']; + protected array $selects = ['$sequence', '$id', '$collection', '$permissions', '$updatedAt']; public static function getName(): string { @@ -63,7 +63,7 @@ class Deletes extends Action ->inject('executionRetention') ->inject('auditRetention') ->inject('log') - ->callback([$this, 'action']); + ->callback($this->action(...)); } /** @@ -277,7 +277,7 @@ class Deletes extends Action $this->deleteByGroup( 'subscribers', [ - Query::equal('topicInternalId', [$topic->getInternalId()]), + Query::equal('topicInternalId', [$topic->getSequence()]), Query::orderAsc(), ], $getProjectDB($project) @@ -298,7 +298,7 @@ class Deletes extends Action private function deleteSessionTargets(Document $project, callable $getProjectDB, Document $session): void { - Targets::delete($getProjectDB($project), Query::equal('sessionInternalId', [$session->getInternalId()])); + Targets::delete($getProjectDB($project), Query::equal('sessionInternalId', [$session->getSequence()])); } /** @@ -434,7 +434,7 @@ class Deletes extends Action public function deleteMemberships(callable $getProjectDB, Document $document, Document $project): void { $dbForProject = $getProjectDB($project); - $teamInternalId = $document->getInternalId(); + $teamInternalId = $document->getSequence(); // Delete Memberships $this->deleteByGroup( @@ -466,7 +466,7 @@ class Deletes extends Action { $projects = $dbForPlatform->find('projects', [ - Query::equal('teamInternalId', [$document->getInternalId()]), + Query::equal('teamInternalId', [$document->getSequence()]), Query::equal('region', [System::getEnv('_APP_REGION', 'default')]) ]); @@ -497,7 +497,7 @@ class Deletes extends Action */ private function deleteProject(Database $dbForPlatform, callable $getProjectDB, Device $deviceForFiles, Device $deviceForSites, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, CertificatesAdapter $certificates, Document $document): void { - $projectInternalId = $document->getInternalId(); + $projectInternalId = $document->getSequence(); $projectId = $document->getId(); try { @@ -637,7 +637,7 @@ class Deletes extends Action private function deleteUser(callable $getProjectDB, Document $document, Document $project): void { $userId = $document->getId(); - $userInternalId = $document->getInternalId(); + $userInternalId = $document->getSequence(); $dbForProject = $getProjectDB($project); // Delete all sessions of this user from the sessions table and update the sessions field of the user record @@ -769,7 +769,7 @@ class Deletes extends Action { $dbForProject = $getProjectDB($project); $siteId = $document->getId(); - $siteInternalId = $document->getInternalId(); + $siteInternalId = $document->getSequence(); /** * Delete rules for site @@ -779,7 +779,7 @@ class Deletes extends Action Query::equal('type', ['deployment']), Query::equal('deploymentResourceType', ['site']), Query::equal('deploymentResourceInternalId', [$siteInternalId]), - Query::equal('projectInternalId', [$project->getInternalId()]) + Query::equal('projectInternalId', [$project->getSequence()]) ], $dbForPlatform, function (Document $document) use ($dbForPlatform, $certificates) { $this->deleteRule($dbForPlatform, $document, $certificates); }); @@ -804,7 +804,7 @@ class Deletes extends Action Query::equal('resourceType', ['site']), Query::orderAsc() ], $dbForProject, function (Document $document) use ($project, $certificates, $deviceForSites, $deviceForBuilds, $deviceForFiles, $dbForPlatform, &$deploymentInternalIds) { - $deploymentInternalIds[] = $document->getInternalId(); + $deploymentInternalIds[] = $document->getSequence(); $deploymentIds[] = $document->getId(); $this->deleteBuildFiles($deviceForBuilds, $document); $this->deleteDeploymentFiles($deviceForSites, $document); @@ -827,7 +827,7 @@ class Deletes extends Action */ Console::info("Deleting VCS repositories and comments linked to site " . $siteId); $this->deleteByGroup('repositories', [ - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), Query::equal('resourceInternalId', [$siteInternalId]), Query::equal('resourceType', ['site']), ], $dbForPlatform, function (Document $document) use ($dbForPlatform) { @@ -855,7 +855,7 @@ class Deletes extends Action $projectId = $project->getId(); $dbForProject = $getProjectDB($project); $functionId = $document->getId(); - $functionInternalId = $document->getInternalId(); + $functionInternalId = $document->getSequence(); /** * Delete rules @@ -865,7 +865,7 @@ class Deletes extends Action Query::equal('type', ['deployment']), Query::equal('deploymentResourceType', ['function']), Query::equal('deploymentResourceInternalId', [$functionInternalId]), - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), Query::orderAsc() ], $dbForPlatform, function (Document $document) use ($project, $dbForPlatform, $certificates) { $this->deleteRule($dbForPlatform, $document, $certificates); @@ -892,7 +892,7 @@ class Deletes extends Action Query::equal('resourceType', ['function']), Query::orderAsc() ], $dbForProject, function (Document $document) use ($dbForPlatform, $project, $certificates, $deviceForFunctions, $deviceForBuilds, &$deploymentInternalIds) { - $deploymentInternalIds[] = $document->getInternalId(); + $deploymentInternalIds[] = $document->getSequence(); $this->deleteDeploymentFiles($deviceForFunctions, $document); $this->deleteBuildFiles($deviceForBuilds, $document); }); @@ -913,7 +913,7 @@ class Deletes extends Action */ Console::info("Deleting VCS repositories and comments linked to function " . $functionId); $this->deleteByGroup('repositories', [ - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), Query::equal('resourceInternalId', [$functionInternalId]), Query::equal('resourceType', ['function']), Query::orderAsc() @@ -957,7 +957,7 @@ class Deletes extends Action } foreach ($screenshotIds as $id) { - $file = ValidatorAuthorization::skip(fn () => $dbForPlatform->getDocument('bucket_' . $bucket->getInternalId(), $id)); + $file = ValidatorAuthorization::skip(fn () => $dbForPlatform->getDocument('bucket_' . $bucket->getSequence(), $id)); if ($file->isEmpty()) { Console::error('Failed to get deployment screenshot: ' . $id); @@ -1060,7 +1060,7 @@ class Deletes extends Action $projectId = $project->getId(); $dbForProject = $getProjectDB($project); $deploymentId = $document->getId(); - $deploymentInternalId = $document->getInternalId(); + $deploymentInternalId = $document->getSequence(); /** * Delete deployment files @@ -1089,7 +1089,7 @@ class Deletes extends Action Query::equal('trigger', ['deployment']), Query::equal('type', ['deployment']), Query::equal('deploymentInternalId', [$deploymentInternalId]), - Query::equal('projectInternalId', [$project->getInternalId()]) + Query::equal('projectInternalId', [$project->getSequence()]) ], $dbForPlatform, function (Document $document) use ($dbForPlatform, $certificates) { $this->deleteRule($dbForPlatform, $document, $certificates); }); @@ -1211,7 +1211,7 @@ class Deletes extends Action { $dbForProject = $getProjectDB($project); - $dbForProject->deleteCollection('bucket_' . $document->getInternalId()); + $dbForProject->deleteCollection('bucket_' . $document->getSequence()); $deviceForFiles->deletePath($document->getId()); } @@ -1229,7 +1229,7 @@ class Deletes extends Action $dbForProject = $getProjectDB($project); $this->listByGroup('functions', [ - Query::equal('installationInternalId', [$document->getInternalId()]) + Query::equal('installationInternalId', [$document->getSequence()]) ], $dbForProject, function ($function) use ($dbForProject, $dbForPlatform) { $dbForPlatform->deleteDocument('repositories', $function->getAttribute('repositoryId')); @@ -1260,7 +1260,7 @@ class Deletes extends Action $this->listByGroup( 'deployments', [ - Query::equal('resourceInternalId', [$function->getInternalId()]), + Query::equal('resourceInternalId', [$function->getSequence()]), Query::equal('resourceType', ['functions']), ], $getProjectDB($project), diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index c8c68a58ba..a9126583ce 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -280,7 +280,7 @@ class Functions extends Action $execution = new Document([ '$id' => $executionId, '$permissions' => $user->isEmpty() ? [] : [Permission::read(Role::user($user->getId()))], - 'resourceInternalId' => $function->getInternalId(), + 'resourceInternalId' => $function->getSequence(), 'resourceId' => $function->getId(), 'resourceType' => 'functions', 'deploymentInternalId' => '', @@ -420,10 +420,10 @@ class Functions extends Action $execution = new Document([ '$id' => $executionId, '$permissions' => $user->isEmpty() ? [] : [Permission::read(Role::user($user->getId()))], - 'resourceInternalId' => $function->getInternalId(), + 'resourceInternalId' => $function->getSequence(), 'resourceId' => $function->getId(), 'resourceType' => 'functions', - 'deploymentInternalId' => $deployment->getInternalId(), + 'deploymentInternalId' => $deployment->getSequence(), 'deploymentId' => $deployment->getId(), 'trigger' => $trigger, 'status' => 'processing', @@ -572,13 +572,13 @@ class Functions extends Action ->setProject($project) ->addMetric(METRIC_EXECUTIONS, 1) ->addMetric(str_replace(['{resourceType}'], [RESOURCE_TYPE_FUNCTIONS], METRIC_RESOURCE_TYPE_EXECUTIONS), 1) - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS), 1) + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS), 1) ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000))// per project ->addMetric(str_replace(['{resourceType}'], [RESOURCE_TYPE_FUNCTIONS], METRIC_RESOURCE_TYPE_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) ->addMetric(METRIC_EXECUTIONS_MB_SECONDS, (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) ->addMetric(str_replace(['{resourceType}'], [RESOURCE_TYPE_FUNCTIONS], METRIC_RESOURCE_TYPE_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) - ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) + ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) ->trigger() ; } diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index 8491e91a6c..2ee107a319 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -373,7 +373,7 @@ class Messaging extends Action throw new \Exception('Storage bucket with the requested ID could not be found'); } - $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); if ($file->isEmpty()) { throw new \Exception('Storage file with the requested ID could not be found'); } @@ -558,7 +558,7 @@ class Messaging extends Action throw new \Exception('Storage bucket with the requested ID could not be found'); } - $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + $file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId); if ($file->isEmpty()) { throw new \Exception('Storage file with the requested ID could not be found'); } diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index b746365f20..f8e302089e 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -279,20 +279,21 @@ class Migrations extends Action ); /** Start Transfer */ - $migration->setAttribute('stage', 'migrating'); - $this->updateMigrationDocument($migration, $projectDocument, $queueForRealtime); - - $transfer->run( - $migration->getAttribute('resources'), - function () use ($migration, $transfer, $projectDocument, $queueForRealtime) { - $migration->setAttribute('resourceData', json_encode($transfer->getCache())); - $migration->setAttribute('statusCounters', json_encode($transfer->getStatusCounters())); - $this->updateMigrationDocument($migration, $projectDocument, $queueForRealtime); - }, - $migration->getAttribute('resourceId'), - $migration->getAttribute('resourceType') - ); + if (empty($source->getErrors())) { + $migration->setAttribute('stage', 'migrating'); + $this->updateMigrationDocument($migration, $projectDocument, $queueForRealtime); + $transfer->run( + $migration->getAttribute('resources'), + function () use ($migration, $transfer, $projectDocument, $queueForRealtime) { + $migration->setAttribute('resourceData', json_encode($transfer->getCache())); + $migration->setAttribute('statusCounters', json_encode($transfer->getStatusCounters())); + $this->updateMigrationDocument($migration, $projectDocument, $queueForRealtime); + }, + $migration->getAttribute('resourceId'), + $migration->getAttribute('resourceType') + ); + } $destination->shutDown(); $source->shutDown(); @@ -368,7 +369,7 @@ class Migrations extends Action $this->updateMigrationDocument($migration, $projectDocument, $queueForRealtime); if ($migration->getAttribute('status', '') === 'failed') { - Console::error('Migration('.$migration->getInternalId().':'.$migration->getId().') failed, Project('.$this->project->getInternalId().':'.$this->project->getId().')'); + Console::error('Migration('.$migration->getSequence().':'.$migration->getId().') failed, Project('.$this->project->getSequence().':'.$this->project->getId().')'); if ($destination) { $destination->error(); diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php index 19d1223d95..c68465f8df 100644 --- a/src/Appwrite/Platform/Workers/StatsResources.php +++ b/src/Appwrite/Platform/Workers/StatsResources.php @@ -80,7 +80,7 @@ class StatsResources extends Action $this->countForProject($dbForPlatform, $getLogsDB, $getProjectDB, $project); $endTime = microtime(true); $executionTime = $endTime - $startTime; - Console::info('Project: ' . $project->getId() . '(' . $project->getInternalId() . ') aggregated in ' . $executionTime .' seconds'); + Console::info('Project: ' . $project->getId() . '(' . $project->getSequence() . ') aggregated in ' . $executionTime .' seconds'); } protected function countForProject(Database $dbForPlatform, callable $getLogsDB, callable $getProjectDB, Document $project): void @@ -105,17 +105,17 @@ class StatsResources extends Action $region = $project->getAttribute('region'); $platforms = $dbForPlatform->count('platforms', [ - Query::equal('projectInternalId', [$project->getInternalId()]) + Query::equal('projectInternalId', [$project->getSequence()]) ]); $webhooks = $dbForPlatform->count('webhooks', [ - Query::equal('projectInternalId', [$project->getInternalId()]) + Query::equal('projectInternalId', [$project->getSequence()]) ]); $keys = $dbForPlatform->count('keys', [ - Query::equal('projectInternalId', [$project->getInternalId()]) + Query::equal('projectInternalId', [$project->getSequence()]) ]); $domains = $dbForPlatform->count('rules', [ - Query::equal('projectInternalId', [$project->getInternalId()]), + Query::equal('projectInternalId', [$project->getSequence()]), Query::equal('owner', ['']), ]); @@ -216,13 +216,13 @@ class StatsResources extends Action $totalFiles = 0; $totalStorage = 0; $this->foreachDocument($dbForProject, 'buckets', [], function ($bucket) use ($dbForProject, $dbForLogs, $region, &$totalFiles, &$totalStorage) { - $files = $dbForProject->count('bucket_' . $bucket->getInternalId()); + $files = $dbForProject->count('bucket_' . $bucket->getSequence()); - $metric = str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES); + $metric = str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES); $this->createStatsDocuments($region, $metric, $files); - $storage = $dbForProject->sum('bucket_' . $bucket->getInternalId(), 'sizeActual'); - $metric = str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE); + $storage = $dbForProject->sum('bucket_' . $bucket->getSequence(), 'sizeActual'); + $metric = str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_STORAGE); $this->createStatsDocuments($region, $metric, $storage); $totalStorage += $storage; @@ -241,10 +241,10 @@ class StatsResources extends Action $totalImageTransformations = 0; $last30Days = (new \DateTime())->sub(\DateInterval::createFromDateString('30 days'))->format('Y-m-d 00:00:00'); $this->foreachDocument($dbForProject, 'buckets', [], function ($bucket) use ($dbForProject, $last30Days, $region, &$totalImageTransformations) { - $imageTransformations = $dbForProject->count('bucket_' . $bucket->getInternalId(), [ + $imageTransformations = $dbForProject->count('bucket_' . $bucket->getSequence(), [ Query::greaterThanEqual('transformedAt', $last30Days), ]); - $metric = str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED); + $metric = str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED); $this->createStatsDocuments($region, $metric, $imageTransformations); $totalImageTransformations += $imageTransformations; }); @@ -260,9 +260,9 @@ class StatsResources extends Action $totalDatabaseStorage = 0; $this->foreachDocument($dbForProject, 'databases', [], function ($database) use ($dbForProject, $region, &$totalCollections, &$totalDocuments, &$totalDatabaseStorage) { - $collections = $dbForProject->count('database_' . $database->getInternalId()); + $collections = $dbForProject->count('database_' . $database->getSequence()); - $metric = str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS); + $metric = str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_COLLECTIONS); $this->createStatsDocuments($region, $metric, $collections); [$documents, $storage] = $this->countForCollections($dbForProject, $database, $region); @@ -280,23 +280,23 @@ class StatsResources extends Action { $databaseDocuments = 0; $databaseStorage = 0; - $this->foreachDocument($dbForProject, 'database_' . $database->getInternalId(), [], function ($collection) use ($dbForProject, $database, $region, &$databaseStorage, &$databaseDocuments) { - $documents = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); - $metric = str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS); + $this->foreachDocument($dbForProject, 'database_' . $database->getSequence(), [], function ($collection) use ($dbForProject, $database, $region, &$databaseStorage, &$databaseDocuments) { + $documents = $dbForProject->count('database_' . $database->getSequence() . '_collection_' . $collection->getSequence()); + $metric = str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getSequence(), $collection->getSequence()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS); $this->createStatsDocuments($region, $metric, $documents); $databaseDocuments += $documents; - $collectionStorage = $dbForProject->getSizeOfCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); - $metric = str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE); + $collectionStorage = $dbForProject->getSizeOfCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence()); + $metric = str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getSequence(), $collection->getSequence()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE); $this->createStatsDocuments($region, $metric, $collectionStorage); $databaseStorage += $collectionStorage; }); - $metric = str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_DOCUMENTS); + $metric = str_replace(['{databaseInternalId}'], [$database->getSequence()], METRIC_DATABASE_ID_DOCUMENTS); $this->createStatsDocuments($region, $metric, $databaseDocuments); - $metric = str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_STORAGE); + $metric = str_replace(['{databaseInternalId}'], [$database->getSequence()], METRIC_DATABASE_ID_STORAGE); $this->createStatsDocuments($region, $metric, $databaseStorage); return [$databaseDocuments, $databaseStorage]; @@ -337,33 +337,33 @@ class StatsResources extends Action $this->foreachDocument($dbForProject, 'functions', [], function (Document $function) use ($dbForProject, $region) { $functionDeploymentsStorage = $dbForProject->sum('deployments', 'sourceSize', [ - Query::equal('resourceInternalId', [$function->getInternalId()]), + Query::equal('resourceInternalId', [$function->getSequence()]), Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]), ]); - $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), $functionDeploymentsStorage); + $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), $functionDeploymentsStorage); $functionDeployments = $dbForProject->count('deployments', [ - Query::equal('resourceInternalId', [$function->getInternalId()]), + Query::equal('resourceInternalId', [$function->getSequence()]), Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]), ]); - $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), $functionDeployments); + $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), $functionDeployments); /** * As deployments and builds have 1-1 relationship, * the count for one should match the other */ - $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS), $functionDeployments); + $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS), $functionDeployments); $functionBuildsStorage = 0; $this->foreachDocument($dbForProject, 'deployments', [ - Query::equal('resourceInternalId', [$function->getInternalId()]), + Query::equal('resourceInternalId', [$function->getSequence()]), Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]), ], function (Document $deployment) use (&$functionBuildsStorage): void { $functionBuildsStorage += $deployment->getAttribute('buildSize', 0); }); - $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), $functionBuildsStorage); + $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), $functionBuildsStorage); }); } @@ -387,29 +387,29 @@ class StatsResources extends Action $this->foreachDocument($dbForProject, 'sites', [], function (Document $site) use ($dbForProject, $region) { $siteDeploymentsStorage = $dbForProject->sum('deployments', 'sourceSize', [ - Query::equal('resourceInternalId', [$site->getInternalId()]), + Query::equal('resourceInternalId', [$site->getSequence()]), Query::equal('resourceType', [RESOURCE_TYPE_SITES]), ]); - $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_SITES,$site->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), $siteDeploymentsStorage); + $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_SITES,$site->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), $siteDeploymentsStorage); $siteDeployments = $dbForProject->count('deployments', [ - Query::equal('resourceInternalId', [$site->getInternalId()]), + Query::equal('resourceInternalId', [$site->getSequence()]), Query::equal('resourceType', [RESOURCE_TYPE_SITES]), ]); - $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_SITES,$site->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), $siteDeployments); + $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_SITES,$site->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), $siteDeployments); /** * As deployments and builds have 1-1 relationship, * the count for one should match the other */ - $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_SITES,$site->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS), $siteDeployments); + $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_SITES,$site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS), $siteDeployments); $siteBuildsStorage = $dbForProject->sum('deployments', 'buildSize', [ - Query::equal('resourceInternalId', [$site->getInternalId()]), + Query::equal('resourceInternalId', [$site->getSequence()]), Query::equal('resourceType', [RESOURCE_TYPE_SITES]), ]); - $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_SITES,$site->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), $siteBuildsStorage); + $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_SITES,$site->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), $siteBuildsStorage); }); } @@ -437,6 +437,6 @@ class StatsResources extends Action $this->documents ); $this->documents = []; - Console::success('Stats written to logs db for project: ' . $project->getId() . '(' . $project->getInternalId() . ')'); + Console::success('Stats written to logs db for project: ' . $project->getId() . '(' . $project->getSequence() . ')'); } } diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php index 07131593e2..59b5a15ada 100644 --- a/src/Appwrite/Platform/Workers/StatsUsage.php +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -145,7 +145,7 @@ class StatsUsage extends Action $aggregationInterval = (int) System::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', '20'); $project = new Document($payload['project'] ?? []); - $projectId = $project->getInternalId(); + $projectId = $project->getSequence(); foreach ($payload['reduce'] ?? [] as $document) { if (empty($document)) { continue; @@ -211,8 +211,8 @@ class StatsUsage extends Action } break; case $document->getCollection() === 'databases': // databases - $collections = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS))); - $documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS))); + $collections = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getSequence(), METRIC_DATABASE_ID_COLLECTIONS))); + $documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getSequence(), METRIC_DATABASE_ID_DOCUMENTS))); if (!empty($collections['value'])) { $metrics[] = [ 'key' => METRIC_COLLECTIONS, @@ -232,7 +232,7 @@ class StatsUsage extends Action $databaseInternalId = $parts[1] ?? 0; $documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace( ['{databaseInternalId}', '{collectionInternalId}'], - [$databaseInternalId, $document->getInternalId()], + [$databaseInternalId, $document->getSequence()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS ))); @@ -249,8 +249,8 @@ class StatsUsage extends Action break; case $document->getCollection() === 'buckets': - $files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES))); - $storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE))); + $files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getSequence(), METRIC_BUCKET_ID_FILES))); + $storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getSequence(), METRIC_BUCKET_ID_FILES_STORAGE))); if (!empty($files['value'])) { $metrics[] = [ @@ -268,13 +268,13 @@ class StatsUsage extends Action break; case $document->getCollection() === 'functions' || $document->getCollection() === 'sites': - $deployments = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getCollection(), $document->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS))); - $deploymentsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getCollection(), $document->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE))); - $builds = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getCollection(), $document->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS))); - $buildsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getCollection(), $document->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE))); - $buildsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getCollection(), $document->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE))); - $executions = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getCollection(), $document->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS))); - $executionsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getCollection(), $document->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE))); + $deployments = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getCollection(), $document->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS))); + $deploymentsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getCollection(), $document->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE))); + $builds = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getCollection(), $document->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS))); + $buildsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getCollection(), $document->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE))); + $buildsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getCollection(), $document->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE))); + $executions = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getCollection(), $document->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS))); + $executionsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getCollection(), $document->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE))); if (!empty($deployments['value'])) { $metrics[] = [ @@ -357,7 +357,7 @@ class StatsUsage extends Action break; } } catch (Throwable $e) { - console::error("[reducer] " . " {DateTime::now()} " . " {$project->getInternalId()} " . " {$e->getMessage()}"); + Console::error("[reducer] " . " {DateTime::now()} " . " {$project->getSequence()} " . " {$e->getMessage()}"); } } @@ -376,7 +376,7 @@ class StatsUsage extends Action continue; } - console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); + Console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getSequence(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); try { foreach ($stats['keys'] ?? [] as $key => $value) { @@ -402,23 +402,23 @@ class StatsUsage extends Action ]); - $this->projects[$project->getInternalId()]['project'] = new Document([ + $this->projects[$project->getSequence()]['project'] = new Document([ '$id' => $project->getId(), - '$internalId' => $project->getInternalId(), + '$sequence' => $project->getSequence(), 'database' => $project->getAttribute('database'), ]); - $this->projects[$project->getInternalId()]['stats'][] = $document; + $this->projects[$project->getSequence()]['stats'][] = $document; $this->prepareForLogsDB($project, $document); } } } catch (Exception $e) { - console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); + Console::error('[' . DateTime::now() . '] project [' . $project->getSequence() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); } } - foreach ($this->projects as $internalId => $projectStats) { - if (empty($internalId)) { + foreach ($this->projects as $sequence => $projectStats) { + if (empty($sequence)) { continue; } try { @@ -427,7 +427,7 @@ class StatsUsage extends Action $dbForProject->createOrUpdateDocumentsWithIncrease('stats', 'value', $projectStats['stats']); Console::success('Batch successfully written to DB'); - unset($this->projects[$internalId]); + unset($this->projects[$sequence]); } catch (Throwable $e) { Console::error('Error processing stats: ' . $e->getMessage()); } @@ -437,7 +437,7 @@ class StatsUsage extends Action } - protected function prepareForLogsDB(Document $project, Document $stat) + protected function prepareForLogsDB(Document $project, Document $stat): void { if (System::getEnv('_APP_STATS_USAGE_DUAL_WRITING', 'disabled') === 'disabled') { return; @@ -451,7 +451,7 @@ class StatsUsage extends Action } } $documentClone = clone $stat; - $documentClone->setAttribute('$tenant', (int) $project->getInternalId()); + $documentClone->setAttribute('$tenant', (int) $project->getSequence()); $this->statDocuments[] = $documentClone; } @@ -462,8 +462,7 @@ class StatsUsage extends Action return; } - $dbForLogs = call_user_func($this->getLogsDB); - $dbForLogs + $dbForLogs = ($this->getLogsDB)() ->setTenant(null) ->setTenantPerDocument(true); @@ -478,6 +477,5 @@ class StatsUsage extends Action } catch (Throwable $th) { Console::error($th->getMessage()); } - $this->register->get('pools')->get('logs')->reclaim(); } } diff --git a/src/Appwrite/Platform/Workers/StatsUsageDump.php b/src/Appwrite/Platform/Workers/StatsUsageDump.php deleted file mode 100644 index b9d486e0d8..0000000000 --- a/src/Appwrite/Platform/Workers/StatsUsageDump.php +++ /dev/null @@ -1,209 +0,0 @@ - true, - METRIC_BUCKETS => true, - METRIC_USERS => true, - METRIC_FUNCTIONS => true, - METRIC_TEAMS => true, - METRIC_MESSAGES => true, - METRIC_MAU => true, - METRIC_WEBHOOKS => true, - METRIC_PLATFORMS => true, - METRIC_PROVIDERS => true, - METRIC_TOPICS => true, - METRIC_KEYS => true, - METRIC_FILES => true, - METRIC_FILES_STORAGE => true, - METRIC_DEPLOYMENTS_STORAGE => true, - METRIC_BUILDS_STORAGE => true, - METRIC_DEPLOYMENTS => true, - METRIC_BUILDS => true, - METRIC_COLLECTIONS => true, - METRIC_DOCUMENTS => true, - METRIC_DATABASES_STORAGE => true, - ]; - - /** - * Skip metrics associated with parent IDs - * these need to be checked individually with `str_ends_with` - */ - protected array $skipParentIdMetrics = [ - '.files', - '.files.storage', - '.collections', - '.documents', - '.deployments', - '.deployments.storage', - '.builds', - '.builds.storage', - '.databases.storage' - ]; - - /** - * @var callable - */ - protected mixed $getLogsDB; - - protected array $periods = [ - '1h' => 'Y-m-d H:00', - '1d' => 'Y-m-d 00:00', - 'inf' => '0000-00-00 00:00' - ]; - - public static function getName(): string - { - return 'stats-usage-dump'; - } - - /** - * @throws \Exception - */ - public function __construct() - { - $this - ->inject('message') - ->inject('getProjectDB') - ->inject('getLogsDB') - ->inject('register') - ->callback([$this, 'action']); - } - - /** - * @param Message $message - * @param callable $getProjectDB - * @param callable $getLogsDB - * @param Registry $register - * @return void - * @throws Exception - * @throws \Throwable - * @throws \Utopia\Database\Exception - */ - public function action(Message $message, callable $getProjectDB, callable $getLogsDB, Registry $register): void - { - $this->getLogsDB = $getLogsDB; - $this->register = $register; - $payload = $message->getPayload() ?? []; - if (empty($payload)) { - throw new Exception('Missing payload'); - } - - foreach ($payload['stats'] ?? [] as $stats) { - $project = new Document($stats['project'] ?? []); - - $numberOfKeys = !empty($stats['keys']) ? count($stats['keys']) : 0; - $receivedAt = $stats['receivedAt'] ?? null; - if ($numberOfKeys === 0) { - continue; - } - - console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); - - try { - /** @var \Utopia\Database\Database $dbForProject */ - $dbForProject = $getProjectDB($project); - foreach ($stats['keys'] ?? [] as $key => $value) { - if ($value == 0) { - continue; - } - - if (str_contains($key, METRIC_DATABASES_STORAGE)) { - continue; - } - - foreach ($this->periods as $period => $format) { - $time = null; - - if ($period !== 'inf') { - $time = !empty($receivedAt) ? (new \DateTime($receivedAt))->format($format) : date($format, time()); - } - $id = \md5("{$time}_{$period}_{$key}"); - - $document = new Document([ - '$id' => $id, - 'period' => $period, - 'time' => $time, - 'metric' => $key, - 'value' => $value, - 'region' => System::getEnv('_APP_REGION', 'default'), - ]); - - $documentClone = clone $document; - - $dbForProject->createOrUpdateDocumentsWithIncrease( - 'stats', - 'value', - [$document] - ); - - $this->writeToLogsDB($project, $documentClone); - } - } - } catch (\Exception $e) { - console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); - } - } - } - - protected function writeToLogsDB(Document $project, Document $document): void - { - if (System::getEnv('_APP_STATS_USAGE_DUAL_WRITING', 'disabled') === 'disabled') { - Console::log('Dual Writing is disabled. Skipping...'); - return; - } - - if (array_key_exists($document->getAttribute('metric'), $this->skipBaseMetrics)) { - return; - } - foreach ($this->skipParentIdMetrics as $skipMetric) { - if (str_ends_with($document->getAttribute('metric'), $skipMetric)) { - return; - } - } - - /** @var \Utopia\Database\Database $dbForLogs*/ - $dbForLogs = call_user_func($this->getLogsDB, $project); - - try { - $dbForLogs->createOrUpdateDocumentsWithIncrease( - 'stats', - 'value', - [$document] - ); - Console::success('Usage logs pushed to Logs DB'); - } catch (\Throwable $th) { - Console::error($th->getMessage()); - } - - $this->register->get('pools')->get('logs')->reclaim(); - } -} diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php index ada4d6faaa..4c081aaa13 100644 --- a/src/Appwrite/Platform/Workers/Webhooks.php +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -176,7 +176,7 @@ class Webhooks extends Action $this->errors[] = $logs; $queueForStatsUsage ->addMetric(METRIC_WEBHOOKS_FAILED, 1) - ->addMetric(str_replace('{webhookInternalId}', $webhook->getInternalId(), METRIC_WEBHOOK_ID_FAILED), 1) + ->addMetric(str_replace('{webhookInternalId}', $webhook->getSequence(), METRIC_WEBHOOK_ID_FAILED), 1) ; @@ -186,7 +186,7 @@ class Webhooks extends Action $dbForPlatform->purgeCachedDocument('projects', $project->getId()); $queueForStatsUsage ->addMetric(METRIC_WEBHOOKS_SENT, 1) - ->addMetric(str_replace('{webhookInternalId}', $webhook->getInternalId(), METRIC_WEBHOOK_ID_SENT), 1) + ->addMetric(str_replace('{webhookInternalId}', $webhook->getSequence(), METRIC_WEBHOOK_ID_SENT), 1) ; } diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index 8dbccf5cbb..af3b5c017e 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -113,6 +113,16 @@ abstract class Format protected function getEnumName(string $service, string $method, string $param): ?string { switch ($service) { + case 'proxy': + switch ($method) { + case 'createRedirectRule': + switch ($param) { + case 'resourceType': + return 'ProxyResourceType'; + } + break; + } + break; case 'console': switch ($method) { case 'getResource': @@ -441,7 +451,13 @@ abstract class Format case 'proxy': switch ($method) { case 'createRedirectRule': - return ['Moved Permanently 301', 'Found 302', 'Temporary Redirect 307', 'Permanent Redirect 308']; + switch ($param) { + case 'statusCode': + return ['Moved Permanently 301', 'Found 302', 'Temporary Redirect 307', 'Permanent Redirect 308']; + case 'resourceType': + return ['Site', 'Function']; + } + break; } break; case 'functions': diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index 31ed14b198..b808a27f98 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -309,7 +309,7 @@ class OpenAPI3 extends Format $bodyRequired = []; foreach ($route->getParams() as $name => $param) { // Set params - if ($param['deprecated']) { + if (($param['deprecated'] ?? false) === true) { continue; } @@ -444,11 +444,11 @@ class OpenAPI3 extends Format $node['schema']['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float'; $node['schema']['x-example'] = $validator->getMin(); break; - case 'Utopia\Validator\Numeric': case 'Utopia\Validator\Integer': $node['schema']['type'] = $validator->getType(); $node['schema']['format'] = 'int32'; break; + case 'Utopia\Validator\Numeric': case 'Utopia\Validator\FloatValidator': $node['schema']['type'] = 'number'; $node['schema']['format'] = 'float'; diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index 86c9420739..4c4016e7e7 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -314,7 +314,7 @@ class Swagger2 extends Format ); foreach ($parameters as $name => $param) { // Set params - if ($param['deprecated']) { + if (($param['deprecated'] ?? false) === true) { continue; } @@ -453,11 +453,11 @@ class Swagger2 extends Format $node['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float'; $node['x-example'] = $validator->getMin(); break; - case 'Utopia\Validator\Numeric': case 'Utopia\Validator\Integer': $node['type'] = $validator->getType(); $node['format'] = 'int32'; break; + case 'Utopia\Validator\Numeric': case 'Utopia\Validator\FloatValidator': $node['type'] = 'number'; $node['format'] = 'float'; diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php index e8eafba5a0..1c5dec44dd 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php @@ -25,7 +25,7 @@ class Base extends Queries { $config = Config::getParam('collections', []); - $collections = array_merge( + $collections = \array_merge( $config['projects'], $config['buckets'], $config['databases'], @@ -34,7 +34,7 @@ class Base extends Queries ); $collection = $collections[$collection]; - // array for constant lookup time + $allowedAttributesLookup = []; foreach ($allowedAttributes as $attribute) { $allowedAttributesLookup[$attribute] = true; @@ -70,10 +70,9 @@ class Base extends Queries 'type' => Database::VAR_DATETIME, 'array' => false, ]); - - $internalId = new Document([ - 'key' => '$internalId', - 'type' => Database::VAR_STRING, + $attributes[] = new Document([ + 'key' => '$sequence', + 'type' => Database::VAR_INTEGER, 'array' => false, ]); @@ -82,7 +81,7 @@ class Base extends Queries new Offset(), new Cursor(), new Filter($attributes, APP_DATABASE_QUERY_MAX_VALUES), - new Order([...$attributes, $internalId]), + new Order($attributes), ]; parent::__construct($validators); diff --git a/src/Appwrite/Utopia/Response/Model/AttributeString.php b/src/Appwrite/Utopia/Response/Model/AttributeString.php index 12bb42009d..fded48fddc 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeString.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeString.php @@ -24,6 +24,13 @@ class AttributeString extends Attribute 'required' => false, 'example' => 'default', ]) + ->addRule('encrypt', [ + 'type' => self::TYPE_BOOLEAN, + 'description' => 'Defines whether this attribute is encrypted or not.', + 'default' => false, + 'required' => false, + 'example' => false, + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/Document.php b/src/Appwrite/Utopia/Response/Model/Document.php index 41a10cee89..d43ab0d1e6 100644 --- a/src/Appwrite/Utopia/Response/Model/Document.php +++ b/src/Appwrite/Utopia/Response/Model/Document.php @@ -36,6 +36,12 @@ class Document extends Any 'default' => '', 'example' => '5e5ea5c16897e', ]) + ->addRule('$sequence', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Document automatically incrementing ID.', + 'default' => 0, + 'example' => 1, + ]) ->addRule('$collectionId', [ 'type' => self::TYPE_STRING, 'description' => 'Collection ID.', @@ -71,7 +77,6 @@ class Document extends Any public function filter(DatabaseDocument $document): DatabaseDocument { - $document->removeAttribute('$internalId'); $document->removeAttribute('$collection'); $document->removeAttribute('$tenant'); diff --git a/src/Appwrite/Vcs/Comment.php b/src/Appwrite/Vcs/Comment.php index 62f6ef61d0..5f528e660f 100644 --- a/src/Appwrite/Vcs/Comment.php +++ b/src/Appwrite/Vcs/Comment.php @@ -36,6 +36,7 @@ class Comment $this->builds[$id] = [ 'projectName' => $project->getAttribute('name'), 'projectId' => $project->getId(), + 'region' => $project->getAttribute('region', 'default'), 'resourceName' => $resource->getAttribute('name'), 'resourceId' => $resource->getId(), 'resourceType' => $resourceType, @@ -66,6 +67,7 @@ class Comment if ($build['resourceType'] === 'site') { $projects[$build['projectId']]['site'][$build['resourceId']] = [ 'name' => $build['resourceName'], + 'region' => $build['region'], 'status' => $build['buildStatus'], 'deploymentId' => $build['deploymentId'], 'action' => $build['action'], @@ -74,6 +76,7 @@ class Comment } elseif ($build['resourceType'] === 'function') { $projects[$build['projectId']]['function'][$build['resourceId']] = [ 'name' => $build['resourceName'], + 'region' => $build['region'], 'status' => $build['buildStatus'], 'deploymentId' => $build['deploymentId'], 'action' => $build['action'], @@ -84,7 +87,7 @@ class Comment $i = 0; foreach ($projects as $projectId => $project) { $protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; - $hostname = System::getEnv('_APP_DOMAIN'); + $hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN')); $text .= "## {$project['name']}\n\n"; $text .= "Project ID: `{$projectId}`\n\n"; @@ -100,10 +103,12 @@ class Comment $text .= "| :- | :- | :- | :- | :- |\n"; foreach ($project['site'] as $siteId => $site) { + $imageStatus = in_array($site['status'], ['processing', 'building']) ? 'building' : $site['status']; + $extension = $site['status'] === 'building' ? 'gif' : 'png'; - $pathLight = '/images/vcs/status-' . $site['status'] . '-light.' . $extension; - $pathDark = '/images/vcs/status-' . $site['status'] . '-dark.' . $extension; + $pathLight = '/images/vcs/status-' . $imageStatus . '-light.' . $extension; + $pathDark = '/images/vcs/status-' . $imageStatus . '-dark.' . $extension; $status = match ($site['status']) { 'waiting' => $this->generatImage($pathLight, $pathDark, 'Queued', 85) . ' _Queued_', @@ -114,7 +119,7 @@ class Comment }; if ($site['action']['type'] === 'logs') { - $action = '[View Logs](' . $protocol . '://' . $hostname . '/console/project-' . $projectId . '/sites/site-' . $siteId . '/deployments/deployment-' . $site['deploymentId'] . ')'; + $action = '[View Logs](' . $protocol . '://' . $hostname . '/console/project-' . $site['region'] . '-' . $projectId . '/sites/site-' . $siteId . '/deployments/deployment-' . $site['deploymentId'] . ')'; } else { $action = '[Authorize](' . $site['action']['url'] . ')'; } @@ -146,12 +151,13 @@ class Comment $text .= "| :- | :- | :- | :- |\n"; foreach ($project['function'] as $functionId => $function) { - $extension = $site['status'] === 'building' ? 'gif' : 'png'; + $imageStatus = in_array($function['status'], ['processing', 'building']) ? 'building' : $function['status']; + $extension = $imageStatus === 'building' ? 'gif' : 'png'; - $pathLight = '/images/vcs/status-' . $site['status'] . '-light.' . $extension; - $pathDark = '/images/vcs/status-' . $site['status'] . '-dark.' . $extension; + $pathLight = '/images/vcs/status-' . $imageStatus . '-light.' . $extension; + $pathDark = '/images/vcs/status-' . $imageStatus . '-dark.' . $extension; - $status = match ($site['status']) { + $status = match ($function['status']) { 'waiting' => $this->generatImage($pathLight, $pathDark, 'Queued', 85) . ' _Queued_', 'processing' => $this->generatImage($pathLight, $pathDark, 'Processing', 85) . ' _Processing_', 'building' => $this->generatImage($pathLight, $pathDark, 'Building', 85) . ' _Building_', @@ -160,12 +166,13 @@ class Comment }; if ($function['action']['type'] === 'logs') { - $action = '[View Logs](' . $protocol . '://' . $hostname . '/console/project-' . $projectId . '/functions/function-' . $functionId . '/deployment-' . $function['deploymentId'] . ')'; + $action = '[View Logs](' . $protocol . '://' . $hostname . '/console/project-' . $function['region'] . '-' . $projectId . '/functions/function-' . $functionId . '/deployment-' . $function['deploymentId'] . ')'; } else { $action = '[Authorize](' . $function['action']['url'] . ')'; } - $text .= "|  **{$function['name']}**
`$functionId`"; + $text .= "|  **{$function['name']}**"; + $text .= "| `{$functionId}`"; $text .= "| {$status}"; $text .= "| {$action}"; $text .= "|\n"; @@ -194,7 +201,7 @@ class Comment public function generatImage(string $pathLight, string $pathDark, string $alt, int $width): string { $protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; - $hostname = System::getEnv('_APP_DOMAIN'); + $hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN')); $imageLight = $protocol . '://' . $hostname . $pathLight; $imageDark = $protocol . '://' . $hostname . $pathDark; diff --git a/src/Executor/Executor.php b/src/Executor/Executor.php index 5b204c1910..15411f18ab 100644 --- a/src/Executor/Executor.php +++ b/src/Executor/Executor.php @@ -19,21 +19,14 @@ class Executor public const METHOD_CONNECT = 'CONNECT'; public const METHOD_TRACE = 'TRACE'; - private bool $selfSigned = false; - - /** - * @var callable(string, string): string $endpoint - */ - private $endpointSelector; + protected bool $selfSigned = false; + protected string $endpoint; protected array $headers; - /** - * @param callable(string, string): string $endpointSelector - */ - public function __construct(callable $endpointSelector) + public function __construct() { - $this->endpointSelector = $endpointSelector; + $this->endpoint = System::getEnv('_APP_EXECUTOR_HOST', ''); $this->headers = [ 'content-type' => 'application/json', 'authorization' => 'Bearer ' . System::getEnv('_APP_EXECUTOR_SECRET', ''), @@ -97,8 +90,8 @@ class Executor 'outputDirectory' => $outputDirectory ]; - $endpoint = $this->selectEndpoint($projectId, $deploymentId); - $response = $this->call($endpoint, self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $timeout); + + $response = $this->call($this->endpoint, self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $timeout); $status = $response['headers']['status-code']; if ($status >= 400) { @@ -128,8 +121,7 @@ class Executor 'timeout' => $timeout ]; - $endpoint = $this->selectEndpoint($projectId, $deploymentId); - $this->call($endpoint, self::METHOD_GET, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $timeout, $callback); + $this->call($this->endpoint, self::METHOD_GET, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $timeout, $callback); } /** @@ -145,8 +137,7 @@ class Executor $runtimeId = "$projectId-$deploymentId" . $suffix; $route = "/runtimes/$runtimeId"; - $endpoint = $this->selectEndpoint($projectId, $deploymentId); - $response = $this->call($endpoint, self::METHOD_DELETE, $route, [ + $response = $this->call($this->endpoint, self::METHOD_DELETE, $route, [ 'x-opr-addressing-method' => 'broadcast' ], [], true, 30); @@ -239,8 +230,7 @@ class Executor $requestTimeout = $timeout + 15; } - $endpoint = $this->selectEndpoint($projectId, $deploymentId); - $response = $this->call($endpoint, self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId, 'content-type' => 'multipart/form-data', 'accept' => 'multipart/form-data' ], $params, true, $requestTimeout); + $response = $this->call($this->endpoint, self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId, 'content-type' => 'multipart/form-data', 'accept' => 'multipart/form-data' ], $params, true, $requestTimeout); $status = $response['headers']['status-code']; if ($status >= 400) { @@ -274,8 +264,7 @@ class Executor 'timeout' => $timeout ]; - $endpoint = $this->selectEndpoint($projectId, $deploymentId); - $response = $this->call($endpoint, self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $timeout); + $response = $this->call($this->endpoint, self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $timeout); $status = $response['headers']['status-code']; if ($status >= 400) { @@ -465,9 +454,4 @@ class Executor return $output; } - - private function selectEndpoint(string $projectId, string $deploymentId): string - { - return call_user_func($this->endpointSelector, $projectId, $deploymentId); - } } diff --git a/tests/e2e/General/HTTPTest.php b/tests/e2e/General/HTTPTest.php index fe600cd0f8..620409bb39 100644 --- a/tests/e2e/General/HTTPTest.php +++ b/tests/e2e/General/HTTPTest.php @@ -31,7 +31,7 @@ class HTTPTest extends Scope $this->assertEquals(204, $response['headers']['status-code']); $this->assertEquals('Appwrite', $response['headers']['server']); $this->assertEquals('GET, POST, PUT, PATCH, DELETE', $response['headers']['access-control-allow-methods']); - $this->assertEquals('Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent', $response['headers']['access-control-allow-headers']); + $this->assertEquals('Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Dev-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent', $response['headers']['access-control-allow-headers']); $this->assertEquals('X-Appwrite-Session, X-Fallback-Cookies', $response['headers']['access-control-expose-headers']); $this->assertEquals('http://localhost', $response['headers']['access-control-allow-origin']); $this->assertEquals('true', $response['headers']['access-control-allow-credentials']); diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index 5549ef800d..6ff9dafada 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -3,7 +3,6 @@ namespace Tests\E2E\General; use Appwrite\Platform\Modules\Compute\Specification; -use Appwrite\Tests\Retry; use CURLFile; use DateTime; use Tests\E2E\Client; @@ -183,40 +182,43 @@ class UsageTest extends Scope /** * @depends testPrepareUsersStats */ - #[Retry(count: 1)] public function testUsersStats(array $data): array { $requestsTotal = $data['requestsTotal']; - $response = $this->client->call( - Client::METHOD_GET, - '/project/usage', - $this->getConsoleHeaders(), - [ - 'period' => '1h', - 'startDate' => self::getToday(), - 'endDate' => self::getTomorrow(), - ] - ); + $this->assertEventually(function () { + $response = $this->client->call( + Client::METHOD_GET, + '/project/usage', + $this->getConsoleHeaders(), + [ + 'period' => '1h', + 'startDate' => self::getToday(), + 'endDate' => self::getTomorrow(), + ] + ); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertGreaterThanOrEqual(31, count($response['body'])); - $this->validateDates($response['body']['network']); - $this->validateDates($response['body']['requests']); - $this->validateDates($response['body']['users']); - $this->assertArrayHasKey('executionsBreakdown', $response['body']); - $this->assertArrayHasKey('bucketsBreakdown', $response['body']); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertGreaterThanOrEqual(31, count($response['body'])); + $this->validateDates($response['body']['network']); + $this->validateDates($response['body']['requests']); + $this->validateDates($response['body']['users']); + $this->assertArrayHasKey('executionsBreakdown', $response['body']); + $this->assertArrayHasKey('bucketsBreakdown', $response['body']); + }); - $response = $this->client->call( - Client::METHOD_GET, - '/users/usage?range=90d', - $this->getConsoleHeaders() - ); + $this->assertEventually(function () { + $response = $this->client->call( + Client::METHOD_GET, + '/users/usage?range=90d', + $this->getConsoleHeaders() + ); - $this->assertEquals('90d', $response['body']['range']); - $this->assertEquals(90, count($response['body']['users'])); - $this->assertEquals(90, count($response['body']['sessions'])); - $this->assertEquals((self::CREATE / 2), $response['body']['users'][array_key_last($response['body']['users'])]['value']); + $this->assertEquals('90d', $response['body']['range']); + $this->assertEquals(90, count($response['body']['users'])); + $this->assertEquals(90, count($response['body']['sessions'])); + $this->assertEquals((self::CREATE / 2), $response['body']['users'][array_key_last($response['body']['users'])]['value']); + }); return array_merge($data, [ 'requestsTotal' => $requestsTotal @@ -359,7 +361,6 @@ class UsageTest extends Scope /** * @depends testPrepareStorageStats */ - #[Retry(count: 10)] public function testStorageStats(array $data): array { $bucketId = $data['bucketId']; @@ -368,44 +369,50 @@ class UsageTest extends Scope $storageTotal = $data['storageTotal']; $filesTotal = $data['filesTotal']; - $response = $this->client->call( - Client::METHOD_GET, - '/project/usage', - $this->getConsoleHeaders(), - [ - 'period' => '1d', - 'startDate' => self::getToday(), - 'endDate' => self::getTomorrow(), - ] - ); + $this->assertEventually(function () use ($requestsTotal, $storageTotal) { + $response = $this->client->call( + Client::METHOD_GET, + '/project/usage', + $this->getConsoleHeaders(), + [ + 'period' => '1d', + 'startDate' => self::getToday(), + 'endDate' => self::getTomorrow(), + ] + ); - $this->assertGreaterThanOrEqual(31, count($response['body'])); - $this->assertEquals(1, count($response['body']['requests'])); - $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); - $this->validateDates($response['body']['requests']); - $this->assertEquals($storageTotal, $response['body']['filesStorageTotal']); + $this->assertGreaterThanOrEqual(31, count($response['body'])); + $this->assertEquals(1, count($response['body']['requests'])); + $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); + $this->validateDates($response['body']['requests']); + $this->assertEquals($storageTotal, $response['body']['filesStorageTotal']); + }); - $response = $this->client->call( - Client::METHOD_GET, - '/storage/usage?range=30d', - $this->getConsoleHeaders() - ); + $this->assertEventually(function () use ($bucketsTotal, $filesTotal, $storageTotal) { + $response = $this->client->call( + Client::METHOD_GET, + '/storage/usage?range=30d', + $this->getConsoleHeaders() + ); - $this->assertEquals($storageTotal, $response['body']['storage'][array_key_last($response['body']['storage'])]['value']); - $this->validateDates($response['body']['storage']); - $this->assertEquals($bucketsTotal, $response['body']['buckets'][array_key_last($response['body']['buckets'])]['value']); - $this->validateDates($response['body']['buckets']); - $this->assertEquals($filesTotal, $response['body']['files'][array_key_last($response['body']['files'])]['value']); - $this->validateDates($response['body']['files']); + $this->assertEquals($storageTotal, $response['body']['storage'][array_key_last($response['body']['storage'])]['value']); + $this->validateDates($response['body']['storage']); + $this->assertEquals($bucketsTotal, $response['body']['buckets'][array_key_last($response['body']['buckets'])]['value']); + $this->validateDates($response['body']['buckets']); + $this->assertEquals($filesTotal, $response['body']['files'][array_key_last($response['body']['files'])]['value']); + $this->validateDates($response['body']['files']); + }); - $response = $this->client->call( - Client::METHOD_GET, - '/storage/' . $bucketId . '/usage?range=30d', - $this->getConsoleHeaders() - ); + $this->assertEventually(function () use ($bucketId, $storageTotal, $filesTotal) { + $response = $this->client->call( + Client::METHOD_GET, + '/storage/' . $bucketId . '/usage?range=30d', + $this->getConsoleHeaders() + ); - $this->assertEquals($storageTotal, $response['body']['storage'][array_key_last($response['body']['storage'])]['value']); - $this->assertEquals($filesTotal, $response['body']['files'][array_key_last($response['body']['files'])]['value']); + $this->assertEquals($storageTotal, $response['body']['storage'][array_key_last($response['body']['storage'])]['value']); + $this->assertEquals($filesTotal, $response['body']['files'][array_key_last($response['body']['files'])]['value']); + }); return $data; } @@ -577,7 +584,7 @@ class UsageTest extends Scope } /** @depends testPrepareDatabaseStats */ - #[Retry(count: 1)] + public function testDatabaseStats(array $data): array { $databaseId = $data['databaseId']; @@ -587,60 +594,66 @@ class UsageTest extends Scope $collectionsTotal = $data['collectionsTotal']; $documentsTotal = $data['documentsTotal']; - sleep(self::WAIT); + $this->assertEventually(function () use ($requestsTotal, $databasesTotal, $documentsTotal) { + $response = $this->client->call( + Client::METHOD_GET, + '/project/usage', + $this->getConsoleHeaders(), + [ + 'period' => '1d', + 'startDate' => self::getToday(), + 'endDate' => self::getTomorrow(), + ] + ); - $response = $this->client->call( - Client::METHOD_GET, - '/project/usage', - $this->getConsoleHeaders(), - [ - 'period' => '1d', - 'startDate' => self::getToday(), - 'endDate' => self::getTomorrow(), - ] - ); + $this->assertGreaterThanOrEqual(31, count($response['body'])); + $this->assertEquals(1, count($response['body']['requests'])); + $this->assertEquals(1, count($response['body']['network'])); + $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); + $this->validateDates($response['body']['requests']); + $this->assertEquals($databasesTotal, $response['body']['databasesTotal']); + $this->assertEquals($documentsTotal, $response['body']['documentsTotal']); + }); - $this->assertGreaterThanOrEqual(31, count($response['body'])); - $this->assertEquals(1, count($response['body']['requests'])); - $this->assertEquals(1, count($response['body']['network'])); - $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); - $this->validateDates($response['body']['requests']); - $this->assertEquals($databasesTotal, $response['body']['databasesTotal']); - $this->assertEquals($documentsTotal, $response['body']['documentsTotal']); + $this->assertEventually(function () use ($collectionsTotal, $databasesTotal, $documentsTotal) { + $response = $this->client->call( + Client::METHOD_GET, + '/databases/usage?range=30d', + $this->getConsoleHeaders() + ); - $response = $this->client->call( - Client::METHOD_GET, - '/databases/usage?range=30d', - $this->getConsoleHeaders() - ); + $this->assertEquals($databasesTotal, $response['body']['databases'][array_key_last($response['body']['databases'])]['value']); + $this->validateDates($response['body']['databases']); + $this->assertEquals($collectionsTotal, $response['body']['collections'][array_key_last($response['body']['collections'])]['value']); + $this->validateDates($response['body']['collections']); + $this->assertEquals($documentsTotal, $response['body']['documents'][array_key_last($response['body']['documents'])]['value']); + $this->validateDates($response['body']['documents']); + }); - $this->assertEquals($databasesTotal, $response['body']['databases'][array_key_last($response['body']['databases'])]['value']); - $this->validateDates($response['body']['databases']); - $this->assertEquals($collectionsTotal, $response['body']['collections'][array_key_last($response['body']['collections'])]['value']); - $this->validateDates($response['body']['collections']); - $this->assertEquals($documentsTotal, $response['body']['documents'][array_key_last($response['body']['documents'])]['value']); - $this->validateDates($response['body']['documents']); + $this->assertEventually(function () use ($databaseId, $collectionsTotal, $documentsTotal) { + $response = $this->client->call( + Client::METHOD_GET, + '/databases/' . $databaseId . '/usage?range=30d', + $this->getConsoleHeaders() + ); - $response = $this->client->call( - Client::METHOD_GET, - '/databases/' . $databaseId . '/usage?range=30d', - $this->getConsoleHeaders() - ); + $this->assertEquals($collectionsTotal, $response['body']['collections'][array_key_last($response['body']['collections'])]['value']); + $this->validateDates($response['body']['collections']); - $this->assertEquals($collectionsTotal, $response['body']['collections'][array_key_last($response['body']['collections'])]['value']); - $this->validateDates($response['body']['collections']); + $this->assertEquals($documentsTotal, $response['body']['documents'][array_key_last($response['body']['documents'])]['value']); + $this->validateDates($response['body']['documents']); + }); - $this->assertEquals($documentsTotal, $response['body']['documents'][array_key_last($response['body']['documents'])]['value']); - $this->validateDates($response['body']['documents']); + $this->assertEventually(function () use ($databaseId, $collectionId, $documentsTotal) { + $response = $this->client->call( + Client::METHOD_GET, + '/databases/' . $databaseId . '/collections/' . $collectionId . '/usage?range=30d', + $this->getConsoleHeaders() + ); - $response = $this->client->call( - Client::METHOD_GET, - '/databases/' . $databaseId . '/collections/' . $collectionId . '/usage?range=30d', - $this->getConsoleHeaders() - ); - - $this->assertEquals($documentsTotal, $response['body']['documents'][array_key_last($response['body']['documents'])]['value']); - $this->validateDates($response['body']['documents']); + $this->assertEquals($documentsTotal, $response['body']['documents'][array_key_last($response['body']['documents'])]['value']); + $this->validateDates($response['body']['documents']); + }); return $data; } @@ -799,67 +812,69 @@ class UsageTest extends Scope } /** @depends testPrepareFunctionsStats */ - #[Retry(count: 1)] public function testFunctionsStats(array $data): array { $functionId = $data['functionId']; $executionTime = $data['executionTime']; $executions = $data['executions']; - $response = $this->client->call( - Client::METHOD_GET, - '/functions/' . $functionId . '/usage?range=30d', - $this->getConsoleHeaders() - ); + $this->assertEventually(function () use ($functionId, $executions, $executionTime) { + $response = $this->client->call( + Client::METHOD_GET, + '/functions/' . $functionId . '/usage?range=30d', + $this->getConsoleHeaders() + ); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(24, count($response['body'])); - $this->assertEquals('30d', $response['body']['range']); - $this->assertIsArray($response['body']['deployments']); - $this->assertIsArray($response['body']['deploymentsStorage']); - $this->assertIsNumeric($response['body']['deploymentsStorageTotal']); - $this->assertIsNumeric($response['body']['buildsMbSecondsTotal']); - $this->assertIsNumeric($response['body']['executionsMbSecondsTotal']); - $this->assertIsArray($response['body']['builds']); - $this->assertIsArray($response['body']['buildsTime']); - $this->assertIsArray($response['body']['buildsMbSeconds']); - $this->assertIsArray($response['body']['executions']); - $this->assertIsArray($response['body']['executionsTime']); - $this->assertIsArray($response['body']['executionsMbSeconds']); - $this->assertEquals($executions, $response['body']['executions'][array_key_last($response['body']['executions'])]['value']); - $this->validateDates($response['body']['executions']); - $this->assertEquals($executionTime, $response['body']['executionsTime'][array_key_last($response['body']['executionsTime'])]['value']); - $this->validateDates($response['body']['executionsTime']); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(24, count($response['body'])); + $this->assertEquals('30d', $response['body']['range']); + $this->assertIsArray($response['body']['deployments']); + $this->assertIsArray($response['body']['deploymentsStorage']); + $this->assertIsNumeric($response['body']['deploymentsStorageTotal']); + $this->assertIsNumeric($response['body']['buildsMbSecondsTotal']); + $this->assertIsNumeric($response['body']['executionsMbSecondsTotal']); + $this->assertIsArray($response['body']['builds']); + $this->assertIsArray($response['body']['buildsTime']); + $this->assertIsArray($response['body']['buildsMbSeconds']); + $this->assertIsArray($response['body']['executions']); + $this->assertIsArray($response['body']['executionsTime']); + $this->assertIsArray($response['body']['executionsMbSeconds']); + $this->assertEquals($executions, $response['body']['executions'][array_key_last($response['body']['executions'])]['value']); + $this->validateDates($response['body']['executions']); + $this->assertEquals($executionTime, $response['body']['executionsTime'][array_key_last($response['body']['executionsTime'])]['value']); + $this->validateDates($response['body']['executionsTime']); + }); - $response = $this->client->call( - Client::METHOD_GET, - '/functions/usage?range=30d', - $this->getConsoleHeaders() - ); + $this->assertEventually(function () use ($executions, $executionTime) { + $response = $this->client->call( + Client::METHOD_GET, + '/functions/usage?range=30d', + $this->getConsoleHeaders() + ); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(25, count($response['body'])); - $this->assertEquals($response['body']['range'], '30d'); - $this->assertIsArray($response['body']['functions']); - $this->assertIsArray($response['body']['deployments']); - $this->assertIsArray($response['body']['deploymentsStorage']); - $this->assertIsArray($response['body']['builds']); - $this->assertIsArray($response['body']['buildsTime']); - $this->assertIsArray($response['body']['buildsMbSeconds']); - $this->assertIsArray($response['body']['executions']); - $this->assertIsArray($response['body']['executionsTime']); - $this->assertIsArray($response['body']['executionsMbSeconds']); - $this->assertEquals($executions, $response['body']['executions'][array_key_last($response['body']['executions'])]['value']); - $this->validateDates($response['body']['executions']); - $this->assertEquals($executionTime, $response['body']['executionsTime'][array_key_last($response['body']['executionsTime'])]['value']); - $this->validateDates($response['body']['executionsTime']); - $this->assertGreaterThan(0, $response['body']['buildsTime'][array_key_last($response['body']['buildsTime'])]['value']); - $this->validateDates($response['body']['buildsTime']); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(25, count($response['body'])); + $this->assertEquals($response['body']['range'], '30d'); + $this->assertIsArray($response['body']['functions']); + $this->assertIsArray($response['body']['deployments']); + $this->assertIsArray($response['body']['deploymentsStorage']); + $this->assertIsArray($response['body']['builds']); + $this->assertIsArray($response['body']['buildsTime']); + $this->assertIsArray($response['body']['buildsMbSeconds']); + $this->assertIsArray($response['body']['executions']); + $this->assertIsArray($response['body']['executionsTime']); + $this->assertIsArray($response['body']['executionsMbSeconds']); + $this->assertEquals($executions, $response['body']['executions'][array_key_last($response['body']['executions'])]['value']); + $this->validateDates($response['body']['executions']); + $this->assertEquals($executionTime, $response['body']['executionsTime'][array_key_last($response['body']['executionsTime'])]['value']); + $this->validateDates($response['body']['executionsTime']); + $this->assertGreaterThan(0, $response['body']['buildsTime'][array_key_last($response['body']['buildsTime'])]['value']); + $this->validateDates($response['body']['buildsTime']); + }); return $data; } - public function testPrepareSitesStats(): array { $siteId = $this->setupSite([ @@ -927,7 +942,6 @@ class UsageTest extends Scope } /** @depends testPrepareSitesStats */ - #[Retry(count: 1)] public function testSitesStats(array $data) { $siteId = $data['siteId']; @@ -935,67 +949,72 @@ class UsageTest extends Scope $executions = $data['executions'] ?? 0; $deploymentsSuccess = $data['deploymentsSuccess']; $deploymentsFailed = $data['deploymentsFailed']; - $response = $this->client->call( - Client::METHOD_GET, - '/sites/' . $siteId . '/usage?range=30d', - $this->getConsoleHeaders() - ); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(30, count($response['body'])); - $this->assertEquals('30d', $response['body']['range']); - $this->assertIsArray($response['body']['deployments']); - $this->assertEquals($deploymentsSuccess, $response['body']['buildsSuccessTotal']); - $this->assertEquals($deploymentsFailed, $response['body']['buildsFailedTotal']); - $this->assertIsArray($response['body']['deploymentsStorage']); - $this->assertIsNumeric($response['body']['deploymentsStorageTotal']); - $this->assertIsNumeric($response['body']['buildsMbSecondsTotal']); - $this->assertIsNumeric($response['body']['executionsMbSecondsTotal']); - $this->assertIsArray($response['body']['builds']); - $this->assertIsArray($response['body']['buildsTime']); - $this->assertIsArray($response['body']['buildsMbSeconds']); - $this->assertIsArray($response['body']['executions']); - $this->assertIsArray($response['body']['executionsTime']); - $this->assertIsArray($response['body']['executionsMbSeconds']); - $this->assertIsArray($response['body']['buildsSuccess']); - $this->assertIsArray($response['body']['buildsFailed']); - $this->assertIsArray($response['body']['requests']); - $this->assertIsArray($response['body']['inbound']); - $this->assertIsArray($response['body']['outbound']); - $this->assertEquals($executions, $response['body']['executions'][array_key_last($response['body']['executions'])]['value']); - $this->validateDates($response['body']['executions']); - $this->assertEquals($executionTime, $response['body']['executionsTime'][array_key_last($response['body']['executionsTime'])]['value']); - $this->validateDates($response['body']['executionsTime']); + $this->assertEventually(function () use ($siteId, $deploymentsSuccess, $deploymentsFailed, $executions, $executionTime) { + $response = $this->client->call( + Client::METHOD_GET, + '/sites/' . $siteId . '/usage?range=30d', + $this->getConsoleHeaders() + ); - $response = $this->client->call( - Client::METHOD_GET, - '/sites/usage?range=30d', - $this->getConsoleHeaders() - ); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(30, count($response['body'])); + $this->assertEquals('30d', $response['body']['range']); + $this->assertIsArray($response['body']['deployments']); + $this->assertEquals($deploymentsSuccess, $response['body']['buildsSuccessTotal']); + $this->assertEquals($deploymentsFailed, $response['body']['buildsFailedTotal']); + $this->assertIsArray($response['body']['deploymentsStorage']); + $this->assertIsNumeric($response['body']['deploymentsStorageTotal']); + $this->assertIsNumeric($response['body']['buildsMbSecondsTotal']); + $this->assertIsNumeric($response['body']['executionsMbSecondsTotal']); + $this->assertIsArray($response['body']['builds']); + $this->assertIsArray($response['body']['buildsTime']); + $this->assertIsArray($response['body']['buildsMbSeconds']); + $this->assertIsArray($response['body']['executions']); + $this->assertIsArray($response['body']['executionsTime']); + $this->assertIsArray($response['body']['executionsMbSeconds']); + $this->assertIsArray($response['body']['buildsSuccess']); + $this->assertIsArray($response['body']['buildsFailed']); + $this->assertIsArray($response['body']['requests']); + $this->assertIsArray($response['body']['inbound']); + $this->assertIsArray($response['body']['outbound']); + $this->assertEquals($executions, $response['body']['executions'][array_key_last($response['body']['executions'])]['value']); + $this->validateDates($response['body']['executions']); + $this->assertEquals($executionTime, $response['body']['executionsTime'][array_key_last($response['body']['executionsTime'])]['value']); + $this->validateDates($response['body']['executionsTime']); + }); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(31, count($response['body'])); - $this->assertEquals($response['body']['range'], '30d'); - $this->assertIsArray($response['body']['sites']); - $this->assertIsArray($response['body']['deployments']); - $this->assertIsArray($response['body']['deploymentsStorage']); - $this->assertIsArray($response['body']['builds']); - $this->assertIsArray($response['body']['buildsTime']); - $this->assertIsArray($response['body']['buildsMbSeconds']); - $this->assertIsArray($response['body']['executions']); - $this->assertIsArray($response['body']['executionsTime']); - $this->assertIsArray($response['body']['executionsMbSeconds']); - $this->assertIsArray($response['body']['buildsSuccess']); - $this->assertIsArray($response['body']['buildsFailed']); - $this->assertIsArray($response['body']['requests']); - $this->assertIsArray($response['body']['inbound']); - $this->assertIsArray($response['body']['outbound']); - $this->assertEquals($executions, $response['body']['executions'][array_key_last($response['body']['executions'])]['value']); - $this->validateDates($response['body']['executions']); - $this->assertEquals($executionTime, $response['body']['executionsTime'][array_key_last($response['body']['executionsTime'])]['value']); - $this->validateDates($response['body']['executionsTime']); - $this->assertGreaterThan(0, $response['body']['buildsTime'][array_key_last($response['body']['buildsTime'])]['value']); - $this->validateDates($response['body']['buildsTime']); + $this->assertEventually(function () use ($executions, $executionTime) { + $response = $this->client->call( + Client::METHOD_GET, + '/sites/usage?range=30d', + $this->getConsoleHeaders() + ); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(31, count($response['body'])); + $this->assertEquals($response['body']['range'], '30d'); + $this->assertIsArray($response['body']['sites']); + $this->assertIsArray($response['body']['deployments']); + $this->assertIsArray($response['body']['deploymentsStorage']); + $this->assertIsArray($response['body']['builds']); + $this->assertIsArray($response['body']['buildsTime']); + $this->assertIsArray($response['body']['buildsMbSeconds']); + $this->assertIsArray($response['body']['executions']); + $this->assertIsArray($response['body']['executionsTime']); + $this->assertIsArray($response['body']['executionsMbSeconds']); + $this->assertIsArray($response['body']['buildsSuccess']); + $this->assertIsArray($response['body']['buildsFailed']); + $this->assertIsArray($response['body']['requests']); + $this->assertIsArray($response['body']['inbound']); + $this->assertIsArray($response['body']['outbound']); + $this->assertEquals($executions, $response['body']['executions'][array_key_last($response['body']['executions'])]['value']); + $this->validateDates($response['body']['executions']); + $this->assertEquals($executionTime, $response['body']['executionsTime'][array_key_last($response['body']['executionsTime'])]['value']); + $this->validateDates($response['body']['executionsTime']); + $this->assertGreaterThan(0, $response['body']['buildsTime'][array_key_last($response['body']['buildsTime'])]['value']); + $this->validateDates($response['body']['buildsTime']); + }); } /** @depends testFunctionsStats */ @@ -1032,30 +1051,33 @@ class UsageTest extends Scope $domain = $rule['body']['domain']; - $response = $this->client->call( - Client::METHOD_GET, - '/functions/' . $functionId . '/usage?range=30d', - $this->getConsoleHeaders() - ); + $this->assertEventually(function () use (&$response, $functionId) { + $response = $this->client->call( + Client::METHOD_GET, + '/functions/' . $functionId . '/usage?range=30d', + $this->getConsoleHeaders() + ); - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(24, count($response['body'])); - $this->assertEquals('30d', $response['body']['range']); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(24, count($response['body'])); + $this->assertEquals('30d', $response['body']['range']); + }); $functionsMetrics = $response['body']; - $response = $this->client->call( - Client::METHOD_GET, - '/project/usage', - $this->getConsoleHeaders(), - [ - 'period' => '1h', - 'startDate' => self::getToday(), - 'endDate' => self::getTomorrow(), - ] - ); - - $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEventually(function () use (&$response) { + $response = $this->client->call( + Client::METHOD_GET, + '/project/usage', + $this->getConsoleHeaders(), + [ + 'period' => '1h', + 'startDate' => self::getToday(), + 'endDate' => self::getTomorrow(), + ] + ); + $this->assertEquals(200, $response['headers']['status-code']); + }); $projectMetrics = $response['body']; @@ -1070,8 +1092,6 @@ class UsageTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - $tries = 0; - $this->assertEventually(function () use ($functionId, $functionsMetrics, $projectMetrics) { // Compare new values with old values $response = $this->client->call( diff --git a/tests/e2e/Scopes/Scope.php b/tests/e2e/Scopes/Scope.php index 6deaa62c05..2fa6416bf7 100644 --- a/tests/e2e/Scopes/Scope.php +++ b/tests/e2e/Scopes/Scope.php @@ -2,6 +2,7 @@ namespace Tests\E2E\Scopes; +use Appwrite\Tests\Async; use Appwrite\Tests\Retryable; use PHPUnit\Framework\TestCase; use Tests\E2E\Client; @@ -10,6 +11,7 @@ use Utopia\Database\Helpers\ID; abstract class Scope extends TestCase { use Retryable; + use Async; protected ?Client $client = null; protected string $endpoint = 'http://localhost/v1'; @@ -43,6 +45,18 @@ abstract class Scope extends TestCase return []; } + protected function assertLastRequest(callable $probe, $timeoutMs = 20_000, $waitMs = 500): array + { + $this->assertEventually(function () use (&$request, $probe) { + $request = json_decode(file_get_contents('http://request-catcher:5000/__last_request__'), true); + $request['data'] = json_decode($request['data'], true); + + call_user_func($probe, $request); + }, $timeoutMs, $waitMs); + + return $request; + } + protected function getLastRequest(): array { sleep(2); diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index 452b725c11..0cc2eb893a 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -2034,7 +2034,6 @@ class AccountCustomClientTest extends Scope $this->assertEquals($response['body']['users'][0]['email'], $email); } - #[Retry(count: 2)] public function testCreatePhone(): array { $number = '+123456789'; @@ -2058,17 +2057,15 @@ class AccountCustomClientTest extends Scope $userId = $response['body']['userId']; - \sleep(7); - - $smsRequest = $this->getLastRequest(); - - $this->assertEquals('http://request-catcher:5000/mock-sms', $smsRequest['url']); - $this->assertEquals('Appwrite Mock Message Sender', $smsRequest['headers']['User-Agent']); - $this->assertEquals('username', $smsRequest['headers']['X-Username']); - $this->assertEquals('password', $smsRequest['headers']['X-Key']); - $this->assertEquals('POST', $smsRequest['method']); - $this->assertEquals('+123456789', $smsRequest['data']['from']); - $this->assertEquals($number, $smsRequest['data']['to']); + $smsRequest = $this->assertLastRequest(function (array $request) use ($number) { + $this->assertEquals('http://request-catcher:5000/mock-sms', $request['url']); + $this->assertEquals('Appwrite Mock Message Sender', $request['headers']['User-Agent']); + $this->assertEquals('username', $request['headers']['X-Username']); + $this->assertEquals('password', $request['headers']['X-Key']); + $this->assertEquals('POST', $request['method']); + $this->assertEquals('+123456789', $request['data']['from']); + $this->assertEquals($number, $request['data']['to']); + }); $data['token'] = $smsRequest['data']['message']; $data['id'] = $userId; @@ -2396,7 +2393,6 @@ class AccountCustomClientTest extends Scope /** * @depends testUpdatePhone */ - #[Retry(count: 3)] public function testPhoneVerification(array $data): array { $session = $data['session'] ?? ''; @@ -2416,10 +2412,10 @@ class AccountCustomClientTest extends Scope $this->assertEmpty($response['body']['secret']); $this->assertTrue((new DatetimeValidator())->isValid($response['body']['expire'])); - $smsRequest = $this->getLastRequest(); - - $message = $smsRequest['data']['message']; - $token = substr($message, 0, 6); + $smsRequest = $this->assertLastRequest(function ($request) { + $this->assertArrayHasKey('data', $request); + $this->assertArrayHasKey('message', $request['data']); + }); /** * Test for FAILURE diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 2b60dee856..12226636e2 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -12,7 +12,6 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Datetime as DatetimeValidator; -use Utopia\Validator\JSON; trait DatabasesBase { @@ -299,7 +298,7 @@ trait DatabasesBase $this->assertEquals($title['body']['type'], 'string'); $this->assertEquals($title['body']['size'], 256); $this->assertEquals($title['body']['required'], true); - + $this->assertFalse($title['body']['encrypt']); $this->assertEquals(202, $description['headers']['status-code']); $this->assertEquals($description['body']['key'], 'description'); $this->assertEquals($description['body']['type'], 'string'); @@ -1646,6 +1645,7 @@ trait DatabasesBase $this->assertEquals($document1['body']['actors'][0], 'Chris Evans'); $this->assertEquals($document1['body']['actors'][1], 'Samuel Jackson'); $this->assertEquals($document1['body']['birthDay'], '1975-06-12T12:12:55.000+00:00'); + $this->assertTrue(array_key_exists('$sequence', $document1['body'])); $this->assertEquals(201, $document2['headers']['status-code']); $this->assertEquals($data['moviesId'], $document2['body']['$collectionId']); @@ -1663,6 +1663,7 @@ trait DatabasesBase $this->assertEquals($document2['body']['birthDay'], null); $this->assertEquals($document2['body']['integers'][0], 50); $this->assertEquals($document2['body']['integers'][1], 60); + $this->assertTrue(array_key_exists('$sequence', $document2['body'])); $this->assertEquals(201, $document3['headers']['status-code']); $this->assertEquals($data['moviesId'], $document3['body']['$collectionId']); @@ -1677,6 +1678,7 @@ trait DatabasesBase $this->assertEquals($document3['body']['actors'][0], 'Tom Holland'); $this->assertEquals($document3['body']['actors'][1], 'Zendaya Maree Stoermer'); $this->assertEquals($document3['body']['birthDay'], '1975-06-12T18:12:55.000+00:00'); // UTC for NY + $this->assertTrue(array_key_exists('$sequence', $document3['body'])); $this->assertEquals(400, $document4['headers']['status-code']); @@ -1702,9 +1704,9 @@ trait DatabasesBase $this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']); $this->assertEquals(2017, $documents['body']['documents'][1]['releaseYear']); $this->assertEquals(2019, $documents['body']['documents'][2]['releaseYear']); - $this->assertFalse(array_key_exists('$internalId', $documents['body']['documents'][0])); - $this->assertFalse(array_key_exists('$internalId', $documents['body']['documents'][1])); - $this->assertFalse(array_key_exists('$internalId', $documents['body']['documents'][2])); + $this->assertTrue(array_key_exists('$sequence', $documents['body']['documents'][0])); + $this->assertTrue(array_key_exists('$sequence', $documents['body']['documents'][1])); + $this->assertTrue(array_key_exists('$sequence', $documents['body']['documents'][2])); $this->assertCount(3, $documents['body']['documents']); foreach ($documents['body']['documents'] as $document) { @@ -1797,7 +1799,7 @@ trait DatabasesBase $this->assertEquals($response['body']['releaseYear'], $document['releaseYear']); $this->assertEquals($response['body']['$permissions'], $document['$permissions']); $this->assertEquals($response['body']['birthDay'], $document['birthDay']); - $this->assertFalse(array_key_exists('$internalId', $response['body'])); + $this->assertTrue(array_key_exists('$sequence', $response['body'])); $this->assertFalse(array_key_exists('$tenant', $response['body'])); } } @@ -1823,6 +1825,22 @@ trait DatabasesBase $this->assertEquals($document['title'], $response['body']['title']); $this->assertEquals($document['releaseYear'], $response['body']['releaseYear']); $this->assertArrayNotHasKey('birthDay', $response['body']); + $sequence = $response['body']['$sequence']; + + // Query by sequence + $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $document['$collectionId'] . '/documents/' . $document['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::equal('$sequence', [$sequence]) + ], + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals($document['title'], $response['body']['title']); + $this->assertEquals($document['releaseYear'], $response['body']['releaseYear']); + $this->assertTrue(array_key_exists('$sequence', $response['body'])); } /** @@ -4060,8 +4078,8 @@ trait DatabasesBase $this->assertArrayNotHasKey('$collection', $person1['body']); $this->assertArrayNotHasKey('$collection', $person1['body']['library']); - $this->assertArrayNotHasKey('$internalId', $person1['body']); - $this->assertArrayNotHasKey('$internalId', $person1['body']['library']); + $this->assertArrayHasKey('$sequence', $person1['body']); + $this->assertArrayHasKey('$sequence', $person1['body']['library']); $documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $person['body']['$id'] . '/documents', array_merge([ 'content-type' => 'application/json', @@ -4978,4 +4996,213 @@ trait DatabasesBase 'x-appwrite-key' => $this->getProject()['apiKey'] ])); } + + /** + * @throws \Exception + */ + public function testIncrementAttribute(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'CounterDatabase' + ]); + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'CounterCollection', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + ], + ]); + $collectionId = $collection['body']['$id']; + + // Add integer attribute + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'count', + 'required' => true, + ]); + + \sleep(3); + + // Create document with initial count = 5 + $doc = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => ID::unique(), + 'data' => [ + 'count' => 5 + ], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + $this->assertEquals(201, $doc['headers']['status-code']); + + $docId = $doc['body']['$id']; + + // Increment by default 1 + $inc = $this->client->call(Client::METHOD_PATCH, "/databases/$databaseId/collections/$collectionId/documents/$docId/count/increment", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ])); + $this->assertEquals(200, $inc['headers']['status-code']); + $this->assertEquals(6, $inc['body']['count']); + + // Verify count = 6 + $get = $this->client->call(Client::METHOD_GET, "/databases/$databaseId/collections/$collectionId/documents/$docId", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $this->assertEquals(6, $get['body']['count']); + + // Increment by custom value 4 + $inc2 = $this->client->call(Client::METHOD_PATCH, "/databases/$databaseId/collections/$collectionId/documents/$docId/count/increment", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), [ + 'value' => 4 + ]); + $this->assertEquals(200, $inc2['headers']['status-code']); + $this->assertEquals(10, $inc2['body']['count']); + + $get2 = $this->client->call(Client::METHOD_GET, "/databases/$databaseId/collections/$collectionId/documents/$docId", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $this->assertEquals(10, $get2['body']['count']); + + // Test max limit exceeded + $err = $this->client->call(Client::METHOD_PATCH, "/databases/$databaseId/collections/$collectionId/documents/$docId/count/increment", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), ['max' => 8]); + $this->assertEquals(400, $err['headers']['status-code']); + + // Test attribute not found + $notFound = $this->client->call(Client::METHOD_PATCH, "/databases/$databaseId/collections/$collectionId/documents/$docId/unknown/increment", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ])); + $this->assertEquals(404, $notFound['headers']['status-code']); + } + + public function testDecrementAttribute(): void + { + $database = $this->client->call(Client::METHOD_POST, '/databases', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ], [ + 'databaseId' => ID::unique(), + 'name' => 'CounterDatabase' + ]); + + $databaseId = $database['body']['$id']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'CounterCollection', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + Permission::read(Role::user($this->getUser()['$id'])), + ], + ]); + + $collectionId = $collection['body']['$id']; + + // Add integer attribute + $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/integer', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'count', + 'required' => true, + ]); + + \sleep(2); + + // Create document with initial count = 10 + $doc = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'documentId' => ID::unique(), + 'data' => ['count' => 10], + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + ], + ]); + + $documentId = $doc['body']['$id']; + + // Decrement by default 1 (count = 10 -> 9) + $dec = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId . '/count/decrement', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ])); + $this->assertEquals(200, $dec['headers']['status-code']); + $this->assertEquals(9, $dec['body']['count']); + + $get = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $this->assertEquals(9, $get['body']['count']); + + // Decrement by custom value 3 (count 9 -> 6) + $dec2 = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId . '/count/decrement', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), [ + 'value' => 3 + ]); + $this->assertEquals(200, $dec2['headers']['status-code']); + $this->assertEquals(6, $dec2['body']['count']); + + $get2 = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $this->assertEquals(6, $get2['body']['count']); + + // Test min limit exceeded + $err = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId . '/count/decrement', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), ['min' => 7]); + $this->assertEquals(400, $err['headers']['status-code']); + + // Test type error on non-numeric attribut + $typeErr = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId . '/count/decrement', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), ['value' => 'not-a-number']); + $this->assertEquals(400, $typeErr['headers']['status-code']); + } + + } diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index 829960b54f..e84c760c86 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -644,7 +644,6 @@ class DatabasesCustomServerTest extends Scope */ public function testCreateEncryptedAttribute(array $data): void { - $databaseId = $data['databaseId']; /** @@ -674,7 +673,6 @@ class DatabasesCustomServerTest extends Scope /** * Test for creating encrypted attributes */ - $attributesPath = '/databases/' . $databaseId . '/collections/' . $actors['body']['$id'] . '/attributes'; $firstName = $this->client->call(Client::METHOD_POST, $attributesPath . '/string', array_merge([ @@ -687,6 +685,19 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); + // checking size test + $lastName = $this->client->call(Client::METHOD_POST, $attributesPath . '/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'lastName', + 'size' => 149, + 'required' => true, + 'encrypt' => true + ]); + $this->assertEquals("Size too small. Encrypted strings require a minimum size of " . APP_DATABASE_ENCRYPT_SIZE_MIN . " characters.", $lastName['body']['message']); + $lastName = $this->client->call(Client::METHOD_POST, $attributesPath . '/string', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -695,9 +706,19 @@ class DatabasesCustomServerTest extends Scope 'key' => 'lastName', 'size' => 256, 'required' => true, - 'encrypt' => true, + 'encrypt' => true ]); + $this->assertTrue($lastName['body']['encrypt']); + sleep(1); + + $response = $this->client->call(Client::METHOD_GET, $attributesPath . '/lastName', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ])); + + $this->assertTrue($response['body']['encrypt']); /** * Check status of every attribute @@ -741,6 +762,24 @@ class DatabasesCustomServerTest extends Scope $this->assertEquals(200, $document['headers']['status-code']); $this->assertEquals('Jonah', $document['body']['firstName']); $this->assertEquals('Jameson', $document['body']['lastName']); + + + $actors = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $actors['body']['$id'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), []); + $attributes = $actors['body']['attributes']; + foreach ($attributes as $attribute) { + $this->assertArrayHasKey('encrypt', $attribute); + if ($attribute['key'] === 'firstName') { + $this->assertFalse($attribute['encrypt']); + } + if ($attribute['key'] === 'lastName') { + $this->assertTrue($attribute['encrypt']); + } + } + } public function testDeleteAttribute(): array diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 475eb9b9b4..27b67d851d 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -49,7 +49,7 @@ trait FunctionsBase 'x-appwrite-key' => $this->getProject()['apiKey'], ])); $this->assertEquals('ready', $deployment['body']['status'], 'Deployment status is not ready, deployment: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT)); - }, 50000, 500); + }, 100000, 500); // Not === so multipart/form-data works fine too if (($params['activate'] ?? false) == true) { diff --git a/tests/e2e/Services/Messaging/MessagingConsoleClientTest.php b/tests/e2e/Services/Messaging/MessagingConsoleClientTest.php index 245eb3e8de..49d13128e2 100644 --- a/tests/e2e/Services/Messaging/MessagingConsoleClientTest.php +++ b/tests/e2e/Services/Messaging/MessagingConsoleClientTest.php @@ -206,15 +206,17 @@ class MessagingConsoleClientTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - $logs = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); + $this->assertEventually(function () use ($topic) { + $logs = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); - $this->assertEquals($logs['headers']['status-code'], 200); - $this->assertIsArray($logs['body']['logs']); - $this->assertCount(2, $logs['body']['logs']); - $this->assertIsNumeric($logs['body']['total']); + $this->assertEquals($logs['headers']['status-code'], 200); + $this->assertIsArray($logs['body']['logs']); + $this->assertCount(2, $logs['body']['logs']); + $this->assertIsNumeric($logs['body']['total']); + }); $logs = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topic['body']['$id'] . '/logs', \array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Migrations/MigrationsBase.php b/tests/e2e/Services/Migrations/MigrationsBase.php index c241b38e3d..b8b9439e64 100644 --- a/tests/e2e/Services/Migrations/MigrationsBase.php +++ b/tests/e2e/Services/Migrations/MigrationsBase.php @@ -871,7 +871,7 @@ trait MigrationsBase $this->assertEquals(1, $deployments['body']['total']); $this->assertEquals('ready', $deployments['body']['deployments'][0]['status'], 'Deployment status is not ready, deployment: ' . json_encode($deployments['body']['deployments'][0], JSON_PRETTY_PRINT)); - }, 50000, 500); + }, 100000, 500); // Attempt execution $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', [ diff --git a/tests/e2e/Services/Proxy/ProxyBase.php b/tests/e2e/Services/Proxy/ProxyBase.php index 44e015b751..9f8e92d56f 100644 --- a/tests/e2e/Services/Proxy/ProxyBase.php +++ b/tests/e2e/Services/Proxy/ProxyBase.php @@ -68,7 +68,7 @@ trait ProxyBase return $rule; } - protected function createRedirectRule(string $domain, string $url, int $statusCode): mixed + protected function createRedirectRule(string $domain, string $url, int $statusCode, string $resourceType, string $resourceId): mixed { $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/redirect', array_merge([ 'content-type' => 'application/json', @@ -77,6 +77,8 @@ trait ProxyBase 'domain' => $domain, 'url' => $url, 'statusCode' => $statusCode, + 'resourceType' => $resourceType, + 'resourceId' => $resourceId, ]); return $rule; @@ -115,9 +117,9 @@ trait ProxyBase return $rule['body']['$id']; } - protected function setupRedirectRule(string $domain, string $url, int $statusCode): string + protected function setupRedirectRule(string $domain, string $url, int $statusCode, string $resourceType, string $resourceId): string { - $rule = $this->createRedirectRule($domain, $url, $statusCode); + $rule = $this->createRedirectRule($domain, $url, $statusCode, $resourceType, $resourceId); $this->assertEquals(201, $rule['headers']['status-code'], 'Failed to setup rule: ' . \json_encode($rule)); diff --git a/tests/e2e/Services/Proxy/ProxyCustomServerTest.php b/tests/e2e/Services/Proxy/ProxyCustomServerTest.php index 9a7ba74ec1..ea310d5449 100644 --- a/tests/e2e/Services/Proxy/ProxyCustomServerTest.php +++ b/tests/e2e/Services/Proxy/ProxyCustomServerTest.php @@ -131,7 +131,9 @@ class ProxyCustomServerTest extends Scope $response = $proxyClient->call(Client::METHOD_GET, '/todos/1'); $this->assertEquals(404, $response['headers']['status-code']); - $ruleId = $this->setupRedirectRule($domain, 'https://jsonplaceholder.typicode.com/todos/1', 301); + $siteId = $this->setupSite()['siteId']; + + $ruleId = $this->setupRedirectRule($domain, 'https://jsonplaceholder.typicode.com/todos/1', 301, 'site', $siteId); $this->assertNotEmpty($ruleId); $response = $proxyClient->call(Client::METHOD_GET, '/todos/1'); @@ -147,7 +149,7 @@ class ProxyCustomServerTest extends Scope $this->assertEquals('https://jsonplaceholder.typicode.com/todos/1', $response['headers']['location']); $domain = \uniqid() . '-redirect-307.custom.localhost'; - $ruleId = $this->setupRedirectRule($domain, 'https://jsonplaceholder.typicode.com/todos/1', 307); + $ruleId = $this->setupRedirectRule($domain, 'https://jsonplaceholder.typicode.com/todos/1', 307, 'site', $siteId); $this->assertNotEmpty($ruleId); $proxyClient = new Client(); @@ -158,6 +160,18 @@ class ProxyCustomServerTest extends Scope $this->assertEquals(307, $response['headers']['status-code']); $this->assertEquals('https://jsonplaceholder.typicode.com/todos/1', $response['headers']['location']); + $rules = $this->listRules([ + 'queries' => [ + Query::equal('type', ['redirect'])->toString(), + Query::equal('trigger', ['manual'])->toString(), + Query::equal('deploymentResourceType', ['site'])->toString(), + Query::equal('deploymentResourceId', [$siteId])->toString(), + ], + ]); + $this->assertEquals(200, $rules['headers']['status-code']); + $this->assertEquals(2, $rules['body']['total']); + + $this->cleanupSite($siteId); $this->cleanupRule($ruleId); } diff --git a/tests/e2e/Services/Sites/SitesBase.php b/tests/e2e/Services/Sites/SitesBase.php index 00edcc1b72..2054744863 100644 --- a/tests/e2e/Services/Sites/SitesBase.php +++ b/tests/e2e/Services/Sites/SitesBase.php @@ -265,7 +265,7 @@ trait SitesBase $this->assertEventually(function () use ($siteId, $deploymentId) { $deployment = $this->getDeployment($siteId, $deploymentId); $this->assertEquals('ready', $deployment['body']['status'], 'Deployment status is not ready, deployment: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT)); - }, 100000, 500); + }, 150000, 500); $this->assertEventually(function () use ($siteId, $deploymentId) { $site = $this->getSite($siteId); diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index dd4efa5932..8459e46c6f 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -1446,7 +1446,7 @@ class SitesCustomServerTest extends Scope $this->assertEquals(404, $response['headers']['status-code']); $this->assertStringContainsString("Page not found", $response['body']); // Title $this->assertStringContainsString("Go to homepage", $response['body']); // Button - $this->assertStringContainsString("Powered by", $response['body']); // Brand + $this->assertStringNotContainsString("Powered by", $response['body']); // Brand $this->cleanupSite($siteId); } @@ -1502,7 +1502,7 @@ class SitesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertStringContainsString("Customized 404 page", $response['body']); - $this->assertStringNotContainsString("Powered by", $response['body']); // Brand + $this->assertStringNotContainsString("Powered by", $response['body']); //brand $this->cleanupSite($siteId); } @@ -2651,4 +2651,46 @@ class SitesCustomServerTest extends Scope $this->cleanupSite($siteId); } + + public function testBuildErrorLogs(): void + { + $siteId = $this->setupSite([ + 'siteId' => ID::unique(), + 'name' => 'Astro SSR site', + 'framework' => 'astro', + 'buildRuntime' => 'node-22', + 'outputDirectory' => './dist', + 'buildCommand' => 'npm run build', + 'installCommand' => 'echo "custom error" && npm install', + 'adapter' => 'ssr', + ]); + $this->assertNotEmpty($siteId); + + $site = $this->getSite($siteId); + $this->assertEquals('200', $site['headers']['status-code']); + + $domain = $this->setupSiteDomain($siteId); + $this->assertNotEmpty($domain); + + $deployment = $this->createDeployment($siteId, [ + 'code' => $this->packageSite('astro-static'), + 'activate' => true + ]); + $this->assertEquals(202, $deployment['headers']['status-code']); + + $deploymentId = $deployment['body']['$id']; + $this->assertNotEmpty($deploymentId); + + $this->assertEventually(function () use ($siteId, $deploymentId) { + $deployment = $this->getDeployment($siteId, $deploymentId); + $this->assertEquals('failed', $deployment['body']['status'], 'Deployment status is failed, deployment: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT)); + }, 100000, 500); + + $deployment = $this->getDeployment($siteId, $deploymentId); + $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertStringContainsString('custom error', $deployment['body']['buildLogs']); + $this->assertStringContainsString('Adapter mismatch', $deployment['body']['buildLogs']); + + $this->cleanupSite($siteId); + } } diff --git a/tests/e2e/Services/Teams/TeamsBase.php b/tests/e2e/Services/Teams/TeamsBase.php index 80ac1621ee..fccfded1e1 100644 --- a/tests/e2e/Services/Teams/TeamsBase.php +++ b/tests/e2e/Services/Teams/TeamsBase.php @@ -64,7 +64,7 @@ trait TeamsBase // Step 4: Assert failure — cannot remove the only OWNER from a team $this->assertEquals(400, $response['headers']['status-code']); - $this->assertEquals('general_argument_invalid', $response['body']['type']); + $this->assertEquals('membership_downgrade_prohibited', $response['body']['type']); $this->assertEquals('There must be at least one owner in the organization.', $response['body']['message']); } diff --git a/tests/e2e/Services/Teams/TeamsConsoleClientTest.php b/tests/e2e/Services/Teams/TeamsConsoleClientTest.php index f096831a76..7949761d7f 100644 --- a/tests/e2e/Services/Teams/TeamsConsoleClientTest.php +++ b/tests/e2e/Services/Teams/TeamsConsoleClientTest.php @@ -65,7 +65,7 @@ class TeamsConsoleClientTest extends Scope /** * Test for SUCCESS */ - $response = $this->client->call(Client::METHOD_POST, '/teams/' . $teamUid . '/memberships', array_merge([ + $developer = $this->client->call(Client::METHOD_POST, '/teams/' . $teamUid . '/memberships', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -75,7 +75,8 @@ class TeamsConsoleClientTest extends Scope 'url' => 'http://localhost:5000/join-us#title' ]); - $this->assertEquals(201, $response['headers']['status-code']); + $developerUserId = $developer['body']['$id']; + $this->assertEquals(201, $developer['headers']['status-code']); $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ 'content-type' => 'application/json', @@ -90,13 +91,26 @@ class TeamsConsoleClientTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - $ownerMembershipUid = $response['body']['memberships'][1]['$id']; + $ownerMembershipUid = $response['body']['memberships'][0]['$id']; $response = $this->client->call(Client::METHOD_DELETE, '/teams/' . $teamUid . '/memberships/' . $ownerMembershipUid, array_merge([ 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); + + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertEquals('membership_deletion_prohibited', $response['body']['type']); + $this->assertEquals('There must be at least one owner in the organization.', $response['body']['message']); + + // Remove the excess developer member to reduce the membership count in `TeamsBaseClient` tests. + // This is necessary because the only owner cannot be removed in the console project / top level team / organization. + $response = $this->client->call(Client::METHOD_DELETE, '/teams/' . $teamUid . '/memberships/' . $developerUserId, array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $this->assertEquals(204, $response['headers']['status-code']); return $data; @@ -109,21 +123,6 @@ class TeamsConsoleClientTest extends Scope $membershipUid = $data['membershipUid'] ?? ''; $session = $data['session'] ?? ''; - /** - * Test for FAILURE - */ - $roles = ['developer']; - $response = $this->client->call(Client::METHOD_PATCH, '/teams/' . $teamUid . '/memberships/' . $membershipUid, array_merge([ - 'origin' => 'http://localhost', - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'roles' => $roles - ]); - - $this->assertEquals(400, $response['headers']['status-code']); - $this->assertEquals('There must be at least one owner in the organization.', $response['body']['message']); - /** * Test for unknown team */ @@ -132,7 +131,7 @@ class TeamsConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'roles' => $roles + 'roles' => ['developer'] ]); $this->assertEquals(404, $response['headers']['status-code']); @@ -145,7 +144,7 @@ class TeamsConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'roles' => $roles + 'roles' => ['developer'] ]); $this->assertEquals(404, $response['headers']['status-code']); @@ -160,7 +159,7 @@ class TeamsConsoleClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, ], [ - 'roles' => $roles + 'roles' => ['developer'] ]); $this->assertEquals(401, $response['headers']['status-code']); @@ -168,4 +167,89 @@ class TeamsConsoleClientTest extends Scope return $data; } + + /** + * @depends testUpdateTeamMembershipRoles + */ + public function testDeleteTeamMembership($data): array + { + $teamUid = $data['teamUid'] ?? ''; + $membershipUid = $data['membershipUid'] ?? ''; + $session = $data['session'] ?? ''; + + $response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(3, $response['body']['total']); + + $ownerMembershipUid = $response['body']['memberships'][0]['$id']; + + /** + * Test deleting a membership that does not exists + */ + $response = $this->client->call(Client::METHOD_DELETE, '/teams/' . $teamUid . '/memberships/dne', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + /** + * Test deleting another user's membership + */ + $response = $this->client->call(Client::METHOD_DELETE, '/teams/' . $teamUid . '/memberships/' . $ownerMembershipUid, [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, + ]); + + $this->assertEquals(401, $response['headers']['status-code']); + $this->assertEquals('The current user is not authorized to perform the requested action.', $response['body']['message']); + + $response = $this->client->call(Client::METHOD_DELETE, '/teams/' . $teamUid . '/memberships/' . $membershipUid, [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, + ]); + + $this->assertEquals(204, $response['headers']['status-code']); + + $response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals(2, $response['body']['total']); + + /** + * Test for when the owner tries to delete their membership + */ + $response = $this->client->call(Client::METHOD_DELETE, '/teams/' . $teamUid . '/memberships/' . $ownerMembershipUid, array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertEquals('membership_deletion_prohibited', $response['body']['type']); + $this->assertEquals('There must be at least one owner in the organization.', $response['body']['message']); + + $response = $this->client->call(Client::METHOD_GET, '/teams/' . $teamUid . '/memberships/' . $ownerMembershipUid, array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $response['headers']['status-code']); + + return []; + } } diff --git a/tests/resources/docker/docker-compose.yml b/tests/resources/docker/docker-compose.yml index 4bbca3e9c0..039ffb731a 100644 --- a/tests/resources/docker/docker-compose.yml +++ b/tests/resources/docker/docker-compose.yml @@ -35,7 +35,7 @@ services: - VERSION=dev restart: unless-stopped ports: - - 9501:80 + - "9501:80" networks: - appwrite labels: @@ -52,15 +52,12 @@ services: - ./phpunit.xml:/usr/src/code/phpunit.xml - ./tests:/usr/src/code/tests - ./app:/usr/src/code/app - # - ./vendor:/usr/src/code/vendor - ./docs:/usr/src/code/docs - ./public:/usr/src/code/public - ./src:/usr/src/code/src - - ./debug:/tmp depends_on: - mariadb - redis - # - clamav environment: - _APP_COMPRESSION_MIN_SIZE_BYTES - _APP_ENV @@ -353,33 +350,6 @@ services: volumes: - appwrite-redis:/data:rw - # clamav: - # image: appwrite/clamav:1.2.0 - # container_name: appwrite-clamav - # restart: unless-stopped - # networks: - # - appwrite - # volumes: - # - appwrite-uploads:/storage/uploads - - - # 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' - networks: gateway: appwrite: