diff --git a/app/config/platforms.php b/app/config/platforms.php index ae09c39cac..525e278cd1 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -25,6 +25,7 @@ return [ 'gitRepoName' => 'sdk-for-web', 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/web/CHANGELOG.md'), 'demos' => [ [ 'icon' => 'react.svg', @@ -73,6 +74,7 @@ return [ 'gitRepoName' => 'sdk-for-flutter', 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/flutter/CHANGELOG.md'), ], [ 'key' => 'apple', @@ -91,6 +93,7 @@ return [ 'gitRepoName' => 'sdk-for-apple', 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/apple/CHANGELOG.md'), ], [ 'key' => 'objective-c', @@ -108,6 +111,7 @@ return [ 'gitRepoName' => 'sdk-for-objective-c', 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/objective-c/CHANGELOG.md'), ], [ 'key' => 'android', @@ -130,6 +134,7 @@ return [ 'Kotlin' => 'kotlin', 'Java' => 'java', ], + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/android/CHANGELOG.md'), ], [ 'key' => 'react-native', @@ -148,6 +153,7 @@ return [ 'gitRepoName' => 'sdk-for-react-native', 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/react-native/CHANGELOG.md'), ], [ 'key' => 'graphql', @@ -167,6 +173,7 @@ return [ 'gitUserName' => '', 'gitBranch' => '', 'isSDK' => false, + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/graphql/CHANGELOG.md'), ], [ 'key' => 'rest', @@ -186,6 +193,7 @@ return [ 'gitUserName' => '', 'gitBranch' => '', 'isSDK' => false, + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/rest/CHANGELOG.md'), ], ], ], @@ -199,8 +207,8 @@ return [ [ 'key' => 'web', 'name' => 'Console', - 'version' => '1.9.0', - 'url' => 'https://github.com/appwrite/sdk-for-console', + 'version' => '', + 'url' => '', 'package' => '', 'enabled' => true, 'beta' => false, @@ -209,10 +217,11 @@ return [ 'family' => APP_PLATFORM_CONSOLE, 'prism' => 'javascript', 'source' => \realpath(__DIR__ . '/../sdks/console-web'), - 'gitUrl' => 'git@github.com:appwrite/sdk-for-console.git', + 'gitUrl' => '', 'gitBranch' => 'dev', - 'gitRepoName' => 'sdk-for-console', - 'gitUserName' => 'appwrite', + 'gitRepoName' => '', + 'gitUserName' => '', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/console/CHANGELOG.md'), ], [ 'key' => 'cli', @@ -232,6 +241,7 @@ return [ 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', 'repoBranch' => 'master', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/cli/CHANGELOG.md'), 'exclude' => [ 'services' => [ ['name' => 'assistant'], @@ -265,6 +275,7 @@ return [ 'gitRepoName' => 'sdk-for-node', 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/nodejs/CHANGELOG.md'), ], [ 'key' => 'deno', @@ -283,6 +294,7 @@ return [ 'gitRepoName' => 'sdk-for-deno', 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/deno/CHANGELOG.md'), ], [ 'key' => 'php', @@ -301,6 +313,7 @@ return [ 'gitRepoName' => 'sdk-for-php', 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/php/CHANGELOG.md'), ], [ 'key' => 'python', @@ -319,6 +332,7 @@ return [ 'gitRepoName' => 'sdk-for-python', 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/python/CHANGELOG.md'), ], [ 'key' => 'ruby', @@ -337,6 +351,7 @@ return [ 'gitRepoName' => 'sdk-for-ruby', 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/ruby/CHANGELOG.md'), ], [ 'key' => 'go', @@ -355,11 +370,12 @@ return [ 'gitRepoName' => 'sdk-for-go', 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/go/CHANGELOG.md'), ], [ 'key' => 'dotnet', 'name' => '.NET', - 'version' => '0.14.1', + 'version' => '0.15.0', 'url' => 'https://github.com/appwrite/sdk-for-dotnet', 'package' => 'https://www.nuget.org/packages/Appwrite', 'enabled' => true, @@ -373,6 +389,7 @@ return [ 'gitRepoName' => 'sdk-for-dotnet', 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/dotnet/CHANGELOG.md'), ], [ 'key' => 'dart', @@ -391,6 +408,7 @@ return [ 'gitRepoName' => 'sdk-for-dart', 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/dart/CHANGELOG.md'), ], [ 'key' => 'kotlin', @@ -413,6 +431,7 @@ return [ 'Kotlin' => 'kotlin', 'Java' => 'java', ], + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/kotlin/CHANGELOG.md'), ], [ 'key' => 'swift', @@ -431,6 +450,7 @@ return [ 'gitRepoName' => 'sdk-for-swift', 'gitUserName' => 'appwrite', 'gitBranch' => 'dev', + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/swift/CHANGELOG.md'), ], [ 'key' => 'graphql', @@ -450,6 +470,7 @@ return [ 'gitUserName' => '', 'gitBranch' => '', 'isSDK' => false, + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/graphql/CHANGELOG.md'), ], [ 'key' => 'rest', @@ -469,6 +490,7 @@ return [ 'gitUserName' => '', 'gitBranch' => '', 'isSDK' => false, + 'changelog' => \realpath(__DIR__ . '/../../docs/sdks/rest/CHANGELOG.md'), ], ], ], diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 6adbf61d6d..61b71930eb 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -2239,7 +2239,30 @@ App::post('/v1/account/tokens/email') ]); $user->removeAttribute('$sequence'); - Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); + $user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); + try { + $target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([ + '$permissions' => [ + Permission::read(Role::user($user->getId())), + Permission::update(Role::user($user->getId())), + Permission::delete(Role::user($user->getId())), + ], + 'userId' => $user->getId(), + 'userInternalId' => $user->getSequence(), + 'providerType' => MESSAGE_TYPE_EMAIL, + 'identifier' => $email, + ]))); + $user->setAttribute('targets', [...$user->getAttribute('targets', []), $target]); + } catch (Duplicate) { + $existingTarget = $dbForProject->findOne('targets', [ + Query::equal('identifier', [$email]), + ]); + if (!$existingTarget->isEmpty()) { + $user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND); + } + } + + $dbForProject->purgeCachedDocument('users', $user->getId()); } $tokenSecret = Auth::codeGenerator(6); diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 3378d011c9..45ca444c1c 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -3453,6 +3453,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') } else { $relation->removeAttribute('$collectionId'); $relation->removeAttribute('$databaseId'); + $relation->removeAttribute('$sequence'); $relation->setAttribute('$collection', $relatedCollection->getId()); $type = Database::PERMISSION_UPDATE; } @@ -3485,6 +3486,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') } } + // Remove sequence if set + unset($document['$sequence']); + // Assign a unique ID if needed, otherwise use the provided ID. $document['$id'] = $sourceId === 'unique()' ? ID::unique() : $sourceId; $document = new Document($document); @@ -4074,6 +4078,9 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $permissions = $document->getPermissions() ?? []; } + // Remove sequence if set + unset($data['$sequence']); + $data['$id'] = $documentId; $data['$permissions'] = $permissions; $newDocument = new Document($data); @@ -4126,6 +4133,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum )); $relation->removeAttribute('$collectionId'); $relation->removeAttribute('$databaseId'); + $relation->removeAttribute('$sequence'); // Attribute $collection is required for Utopia. $relation->setAttribute( '$collection', @@ -4708,6 +4716,9 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents') } } + // Remove sequence if set + unset($data['$sequence']); + $documents = []; try { diff --git a/app/controllers/general.php b/app/controllers/general.php index 446ffd9058..1e78f07dcf 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1385,9 +1385,10 @@ App::get('/robots.txt') ->inject('apiKey') ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) { $host = $request->getHostname() ?? ''; + $consoleDomain = System::getEnv('_APP_CONSOLE_DOMAIN', ''); $mainDomain = System::getEnv('_APP_DOMAIN', ''); - if (($host === $mainDomain || $host === 'localhost') && empty($previewHostname)) { + if (($host === $consoleDomain || $host === $mainDomain || $host === 'localhost') && empty($previewHostname)) { $template = new View(__DIR__ . '/../views/general/robots.phtml'); $response->text($template->render(false)); } else { @@ -1418,9 +1419,10 @@ App::get('/humans.txt') ->inject('apiKey') ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) { $host = $request->getHostname() ?? ''; + $consoleDomain = System::getEnv('_APP_CONSOLE_DOMAIN', ''); $mainDomain = System::getEnv('_APP_DOMAIN', ''); - if (($host === $mainDomain || $host === 'localhost') && empty($previewHostname)) { + if (($host === $consoleDomain || $host === $mainDomain || $host === 'localhost') && empty($previewHostname)) { $template = new View(__DIR__ . '/../views/general/humans.phtml'); $response->text($template->render(false)); } else { diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 5f88440c2c..5d97ad096d 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -31,6 +31,7 @@ use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; use Utopia\Queue\Publisher; use Utopia\System\System; +use Utopia\Telemetry\Adapter as Telemetry; use Utopia\Validator\WhiteList; $parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) { @@ -424,7 +425,8 @@ App::init() ->inject('apiKey') ->inject('plan') ->inject('devKey') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey) use ($usageDatabaseListener, $eventDatabaseListener) { + ->inject('telemetry') + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry) use ($usageDatabaseListener, $eventDatabaseListener) { $route = $utopia->getRoute(); @@ -557,6 +559,7 @@ App::init() )); $useCache = $route->getLabel('cache', false); + $storageCacheOperationsCounter = $telemetry->createCounter('storage.cache.operations.load'); if ($useCache) { $route = $utopia->match($request); $isImageTransformation = $route->getPath() === '/v1/storage/buckets/:bucketId/files/:fileId/preview'; @@ -622,10 +625,12 @@ App::init() ->addHeader('Cache-Control', sprintf('private, max-age=%d', $timestamp)) ->addHeader('X-Appwrite-Cache', 'hit') ->setContentType($cacheLog->getAttribute('mimeType')); + $storageCacheOperationsCounter->add(1, ['result' => 'hit']); if (!$isImageTransformation || !$isDisabled) { $response->send($data); } } else { + $storageCacheOperationsCounter->add(1, ['result' => 'miss']); $response ->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate') ->addHeader('Pragma', 'no-cache') diff --git a/composer.lock b/composer.lock index 8bc8ffaebe..da084c8fcd 100644 --- a/composer.lock +++ b/composer.lock @@ -2547,16 +2547,16 @@ }, { "name": "symfony/http-client", - "version": "v7.3.1", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "4403d87a2c16f33345dca93407a8714ee8c05a64" + "reference": "1c064a0c67749923483216b081066642751cc2c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/4403d87a2c16f33345dca93407a8714ee8c05a64", - "reference": "4403d87a2c16f33345dca93407a8714ee8c05a64", + "url": "https://api.github.com/repos/symfony/http-client/zipball/1c064a0c67749923483216b081066642751cc2c7", + "reference": "1c064a0c67749923483216b081066642751cc2c7", "shasum": "" }, "require": { @@ -2622,7 +2622,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.3.1" + "source": "https://github.com/symfony/http-client/tree/v7.3.2" }, "funding": [ { @@ -2633,12 +2633,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-28T07:58:39+00:00" + "time": "2025-07-15T11:36:08+00:00" }, { "name": "symfony/http-client-contracts", @@ -3993,16 +3997,16 @@ }, { "name": "utopia-php/migration", - "version": "0.11.1", + "version": "0.11.3", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "d528a454d5c1ed6564b2843a39ff13297bcdb1af" + "reference": "fb14e0e35061bfec2656200e3d02a3bdf569a0ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/d528a454d5c1ed6564b2843a39ff13297bcdb1af", - "reference": "d528a454d5c1ed6564b2843a39ff13297bcdb1af", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/fb14e0e35061bfec2656200e3d02a3bdf569a0ac", + "reference": "fb14e0e35061bfec2656200e3d02a3bdf569a0ac", "shasum": "" }, "require": { @@ -4043,9 +4047,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.11.1" + "source": "https://github.com/utopia-php/migration/tree/0.11.3" }, - "time": "2025-07-11T13:46:37+00:00" + "time": "2025-07-30T11:20:31+00:00" }, { "name": "utopia-php/orchestration", @@ -4810,16 +4814,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.41.23", + "version": "0.41.27", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "188dff738ff7b9f1f9209b34ac2092b5456b1001" + "reference": "083fd2e8163d6a4e59ee971ac6cb97277d831dd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/188dff738ff7b9f1f9209b34ac2092b5456b1001", - "reference": "188dff738ff7b9f1f9209b34ac2092b5456b1001", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/083fd2e8163d6a4e59ee971ac6cb97277d831dd5", + "reference": "083fd2e8163d6a4e59ee971ac6cb97277d831dd5", "shasum": "" }, "require": { @@ -4855,9 +4859,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.41.23" + "source": "https://github.com/appwrite/sdk-generator/tree/0.41.27" }, - "time": "2025-07-25T06:47:04+00:00" + "time": "2025-07-31T10:20:46+00:00" }, { "name": "doctrine/annotations", @@ -5336,16 +5340,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.5.0", + "version": "v5.6.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" + "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/221b0d0fdf1369c71047ad1d18bb5880017bbc56", + "reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56", "shasum": "" }, "require": { @@ -5388,9 +5392,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.0" }, - "time": "2025-05-31T08:24:38+00:00" + "time": "2025-07-27T20:03:57+00:00" }, { "name": "phar-io/manifest", @@ -7258,16 +7262,16 @@ }, { "name": "symfony/console", - "version": "v7.3.1", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101" + "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/9e27aecde8f506ba0fd1d9989620c04a87697101", - "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101", + "url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1", + "reference": "5f360ebc65c55265a74d23d7fe27f957870158a1", "shasum": "" }, "require": { @@ -7332,7 +7336,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.1" + "source": "https://github.com/symfony/console/tree/v7.3.2" }, "funding": [ { @@ -7343,25 +7347,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T19:55:54+00:00" + "time": "2025-07-30T17:13:41+00:00" }, { "name": "symfony/filesystem", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd", + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd", "shasum": "" }, "require": { @@ -7398,7 +7406,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.3.0" + "source": "https://github.com/symfony/filesystem/tree/v7.3.2" }, "funding": [ { @@ -7409,25 +7417,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-25T15:15:23+00:00" + "time": "2025-07-07T08:17:47+00:00" }, { "name": "symfony/finder", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d" + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ec2344cf77a48253bbca6939aa3d2477773ea63d", - "reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d", + "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", "shasum": "" }, "require": { @@ -7462,7 +7474,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.3.0" + "source": "https://github.com/symfony/finder/tree/v7.3.2" }, "funding": [ { @@ -7473,25 +7485,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-30T19:00:26+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "afb9a8038025e5dbc657378bfab9198d75f10fca" + "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/afb9a8038025e5dbc657378bfab9198d75f10fca", - "reference": "afb9a8038025e5dbc657378bfab9198d75f10fca", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37", + "reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37", "shasum": "" }, "require": { @@ -7529,7 +7545,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.3.0" + "source": "https://github.com/symfony/options-resolver/tree/v7.3.2" }, "funding": [ { @@ -7540,12 +7556,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-04T13:12:05+00:00" + "time": "2025-07-15T11:36:08+00:00" }, { "name": "symfony/polyfill-ctype", @@ -7924,16 +7944,16 @@ }, { "name": "symfony/string", - "version": "v7.3.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125" + "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125", + "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca", + "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca", "shasum": "" }, "require": { @@ -7991,7 +8011,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.0" + "source": "https://github.com/symfony/string/tree/v7.3.2" }, "funding": [ { @@ -8002,12 +8022,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-20T20:19:01+00:00" + "time": "2025-07-10T08:47:49+00:00" }, { "name": "textalk/websocket", diff --git a/docs/sdks/dotnet/CHANGELOG.md b/docs/sdks/dotnet/CHANGELOG.md index 935bd6c8de..bbfee108a7 100644 --- a/docs/sdks/dotnet/CHANGELOG.md +++ b/docs/sdks/dotnet/CHANGELOG.md @@ -1,10 +1,11 @@ # Change Log -## 0.14.1 +## 0.15.0 * Add `incrementDocumentAttribute` and `decrementDocumentAttribute` support to `Databases` service * Add `encrypt` support to `StringAttribute` model * Add `sequence` support to `Document` model +* Fix: pass enum value as string in API params ## 0.14.0 diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index e6def793b5..036e8783d4 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -122,6 +122,19 @@ class Maintenance extends Action Console::info("[{$time}] Found " . \count($certificates) . " certificates for renewal, scheduling jobs."); foreach ($certificates as $certificate) { + $domain = $certificate->getAttribute('domain'); + if (System::getEnv('_APP_RULES_FORMAT') === 'md5') { + $rule = $dbForPlatform->getDocument('rules', md5($domain)); + } else { + $rule = $dbForPlatform->findOne('rules', [ + Query::equal('domain', [$domain]), + ]); + } + + if ($rule->isEmpty() || $rule->getAttribute('region') !== System::getEnv('_APP_REGION', 'default')) { + continue; + } + $queueForCertificate ->setDomain(new Document([ 'domain' => $certificate->getAttribute('domain') diff --git a/src/Appwrite/Platform/Tasks/SDKs.php b/src/Appwrite/Platform/Tasks/SDKs.php index 98a1ae6a10..c6f4e92186 100644 --- a/src/Appwrite/Platform/Tasks/SDKs.php +++ b/src/Appwrite/Platform/Tasks/SDKs.php @@ -98,7 +98,7 @@ class SDKs extends Action $gettingStarted = ($gettingStarted) ? \file_get_contents($gettingStarted) : ''; $examples = \realpath(__DIR__ . '/../../../../docs/sdks/' . $language['key'] . '/EXAMPLES.md'); $examples = ($examples) ? \file_get_contents($examples) : ''; - $changelog = \realpath(__DIR__ . '/../../../../docs/sdks/' . $language['key'] . '/CHANGELOG.md'); + $changelog = $language['changelog'] ?? ''; $changelog = ($changelog) ? \file_get_contents($changelog) : '# Change Log'; $warning = '**This SDK is compatible with Appwrite server version ' . $version . '. For older versions, please check [previous releases](' . $language['url'] . '/releases).**'; $license = 'BSD-3-Clause'; diff --git a/src/Appwrite/Utopia/Response/Model/Document.php b/src/Appwrite/Utopia/Response/Model/Document.php index d43ab0d1e6..c36d6de12e 100644 --- a/src/Appwrite/Utopia/Response/Model/Document.php +++ b/src/Appwrite/Utopia/Response/Model/Document.php @@ -79,6 +79,7 @@ class Document extends Any { $document->removeAttribute('$collection'); $document->removeAttribute('$tenant'); + $document->setAttribute('$sequence', (int)$document->getAttribute('$sequence', 0)); foreach ($document->getAttributes() as $attribute) { if (\is_array($attribute)) { diff --git a/tests/e2e/Services/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php index 7c83edf3e3..8813e2784f 100644 --- a/tests/e2e/Services/Account/AccountBase.php +++ b/tests/e2e/Services/Account/AccountBase.php @@ -39,6 +39,8 @@ trait AccountBase $this->assertEquals($response['body']['labels'], []); $this->assertArrayHasKey('accessedAt', $response['body']); $this->assertNotEmpty($response['body']['accessedAt']); + $this->assertArrayHasKey('targets', $response['body']); + $this->assertEquals($email, $response['body']['targets'][0]['identifier']); /** * Test for FAILURE @@ -159,7 +161,7 @@ trait AccountBase 'email' => 'otpuser@appwrite.io' ]); - $this->assertEquals(201, $response['headers']['status-code'], ); + $this->assertEquals(201, $response['headers']['status-code']); $this->assertNotEmpty($response['body']['$id']); $this->assertNotEmpty($response['body']['$createdAt']); $this->assertNotEmpty($response['body']['userId']); @@ -209,6 +211,8 @@ trait AccountBase $this->assertEquals($userId, $response['body']['$id']); $this->assertEquals($userId, $response['body']['$id']); $this->assertTrue($response['body']['emailVerification']); + $this->assertArrayHasKey('targets', $response['body']); + $this->assertEquals('otpuser@appwrite.io', $response['body']['targets'][0]['identifier']); $response = $this->client->call(Client::METHOD_POST, '/account/sessions/token', array_merge([ 'origin' => 'http://localhost', diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index be982ac145..de5d33f29d 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -1657,6 +1657,7 @@ trait DatabasesBase $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->assertIsInt($document1['body']['$sequence']); $this->assertEquals(201, $document2['headers']['status-code']); $this->assertEquals($data['moviesId'], $document2['body']['$collectionId']); diff --git a/tests/e2e/Services/Migrations/MigrationsBase.php b/tests/e2e/Services/Migrations/MigrationsBase.php index 6bc1f2d427..5f40662ec3 100644 --- a/tests/e2e/Services/Migrations/MigrationsBase.php +++ b/tests/e2e/Services/Migrations/MigrationsBase.php @@ -901,9 +901,9 @@ trait MigrationsBase /** * Import documents from a CSV file. */ - public function testCreateCsvMigration(): array + public function testCreateCsvMigration(): void { - // make a database + // Make a database $response = $this->client->call(Client::METHOD_POST, '/databases', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -992,6 +992,7 @@ trait MigrationsBase 'missing-row' => $bucketOneId, 'missing-column' => $bucketOneId, 'irrelevant-column' => $bucketOneId, + 'documents-internals' => $bucketOneId, ]; $fileIds = []; @@ -1000,7 +1001,8 @@ trait MigrationsBase $csvFileName = match ($label) { 'missing-row', 'missing-column', - 'irrelevant-column' => "{$label}.csv", + 'irrelevant-column', + 'documents-internals' => "{$label}.csv", default => 'documents.csv', }; @@ -1112,7 +1114,7 @@ trait MigrationsBase ); }, 60000, 500); - // all data exists, pass/ + // all data exists, pass. $migration = $this->performCsvMigration( [ 'endpoint' => 'http://localhost/v1', @@ -1122,32 +1124,8 @@ trait MigrationsBase ] ); - $this->assertEmpty($migration['body']['statusCounters']); - $this->assertEquals('CSV', $migration['body']['source']); - $this->assertEquals('pending', $migration['body']['status']); - $this->assertEquals('Appwrite', $migration['body']['destination']); - $this->assertContains(Resource::TYPE_DOCUMENT, $migration['body']['resources']); - - return [ - 'databaseId' => $databaseId, - 'collectionId' => $collectionId, - 'migrationId' => $migration['body']['$id'], - ]; - } - - /** - * @depends testCreateCsvMigration - */ - public function testImportSuccessful(array $response): void - { - $databaseId = $response['databaseId']; - $collectionId = $response['collectionId']; - $migrationId = $response['migrationId']; - - $documentsCountInCSV = 100; - - // get migration stats - $this->assertEventually(function () use ($migrationId, $databaseId, $collectionId, $documentsCountInCSV) { + $this->assertEventually(function () use ($migration, $databaseId, $collectionId) { + $migrationId = $migration['body']['$id']; $migration = $this->client->call(Client::METHOD_GET, '/migrations/'.$migrationId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1160,8 +1138,8 @@ trait MigrationsBase $this->assertEquals('Appwrite', $migration['body']['destination']); $this->assertContains(Resource::TYPE_DOCUMENT, $migration['body']['resources']); $this->assertArrayHasKey(Resource::TYPE_DOCUMENT, $migration['body']['statusCounters']); - $this->assertEquals($documentsCountInCSV, $migration['body']['statusCounters'][Resource::TYPE_DOCUMENT]['success']); - }, 60000, 500); + $this->assertEquals(100, $migration['body']['statusCounters'][Resource::TYPE_DOCUMENT]['success']); + }, 10_000, 500); // get documents count $documents = $this->client->call(Client::METHOD_GET, '/databases/'.$databaseId.'/collections/'.$collectionId.'/documents', array_merge([ @@ -1169,7 +1147,6 @@ trait MigrationsBase 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'queries' => [ - // there should be only 100! Query::limit(150)->toString() ] ]); @@ -1177,7 +1154,34 @@ trait MigrationsBase $this->assertEquals(200, $documents['headers']['status-code']); $this->assertIsArray($documents['body']['documents']); $this->assertIsNumeric($documents['body']['total']); - $this->assertEquals($documentsCountInCSV, $documents['body']['total']); + $this->assertEquals(100, $documents['body']['total']); + + // all data exists and includes internals, pass. + $migration = $this->performCsvMigration( + [ + 'endpoint' => 'http://localhost/v1', + 'fileId' => $fileIds['documents-internals'], + 'bucketId' => $bucketIds['documents-internals'], + 'resourceId' => $databaseId . ':' . $collectionId, + ] + ); + + $this->assertEventually(function () use ($migration, $databaseId, $collectionId) { + $migrationId = $migration['body']['$id']; + $migration = $this->client->call(Client::METHOD_GET, '/migrations/'.$migrationId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $migration['headers']['status-code']); + $this->assertEquals('finished', $migration['body']['stage']); + $this->assertEquals('completed', $migration['body']['status']); + $this->assertEquals('CSV', $migration['body']['source']); + $this->assertEquals('Appwrite', $migration['body']['destination']); + $this->assertContains(Resource::TYPE_DOCUMENT, $migration['body']['resources']); + $this->assertArrayHasKey(Resource::TYPE_DOCUMENT, $migration['body']['statusCounters']); + $this->assertEquals(25, $migration['body']['statusCounters'][Resource::TYPE_DOCUMENT]['success']); + }, 10_000, 500); } private function performCsvMigration(array $body): array diff --git a/tests/resources/csv/documents-internals.csv b/tests/resources/csv/documents-internals.csv new file mode 100644 index 0000000000..22144ab0b8 --- /dev/null +++ b/tests/resources/csv/documents-internals.csv @@ -0,0 +1,26 @@ +$id,$createdAt,$updatedAt,$permissions,name,age +z1y2x3w4v5u6t7s8,2022-10-23T10:33:01+00:00,2023-03-15T12:00:41+00:00,"read(\"any\"),update(\"user:123\")",Diamond Mendez,56 +r9q0p1o2n3m4l5k6,2021-08-11T21:05:13+00:00,2024-01-02T08:45:22+00:00,"read(\"any\"),update(\"user:456\")",Michael Huff,20 +j7i8h9g0f1e2d3c4,2020-05-29T14:22:56+00:00,2022-11-30T18:19:33+00:00,"read(\"any\")",Alyssa Rodriguez,37 +b5a6z7y8x9w0v1u2,2023-01-18T03:44:09+00:00,2023-09-07T23:50:17+00:00,"read(\"any\")",Barbara Smith,26 +t3s4r5q6p7o8n9m0,2020-11-02T09:12:45+00:00,2021-07-21T15:30:55+00:00,"read(\"any\")",Evelyn Edwards,54 +l1k2j3i4h5g6f7e8,2022-03-19T19:55:27+00:00,2024-05-14T06:28:11+00:00,"read(\"any\")",Tina Richardson,41 +d9c0b1a2z3y4x5w6,2021-04-07T01:18:34+00:00,2023-06-25T11:47:04+00:00,"read(\"any\")",Joel Hernandez,49 +v7u8t9s0r1q2p3o4,2023-08-22T16:40:18+00:00,2024-02-19T04:09:58+00:00,"read(\"any\")",Zachary Cooper,59 +n5m6l7k8j9i0h1g2,2020-02-12T07:59:01+00:00,2022-09-08T13:21:49+00:00,"read(\"any\")",Brittany Spears,20 +f3e4d5c6b7a8z9y0,2021-12-05T22:33:12+00:00,2023-11-11T02:55:37+00:00,"read(\"any\")",Holly White,47 +x1w2v3u4t5s6r7q8,2022-07-14T05:01:50+00:00,2024-04-01T20:10:26+00:00,"read(\"any\")",Kimberly Barnes,27 +p9o0n1m2l3k4j5i6,2020-09-28T11:27:36+00:00,2021-10-17T09:38:08+00:00,"read(\"any\")",Stephen Miller,53 +h7g8f9e0d1c2b3a4,2023-04-04T08:15:59+00:00,2024-06-29T17:03:14+00:00,"read(\"any\")",Yvonne Newman,41 +y5x6w7v8u9t0s1r2,2021-01-25T18:09:21+00:00,2022-08-16T22:44:51+00:00,"read(\"any\")",Carol Kane,38 +q3p4o5n6m7l8k9j0,2022-06-09T12:53:47+00:00,2023-12-24T01:16:05+00:00,"read(\"any\")",Doris Foster,44 +i1h2g3f4e5d6c7b8,2020-07-03T23:37:02+00:00,2021-05-09T05:52:43+00:00,"read(\"any\")",Joseph Stokes,28 +a9z0y1x2w3v4u5t6,2023-10-10T02:20:15+00:00,2024-03-28T14:33:29+00:00,"read(\"any\")",Steve Williams,31 +s7r8q9p0o1n2m3l4,2021-06-16T13:48:53+00:00,2022-04-22T07:07:19+00:00,"read(\"any\")",James Carey,29 +k5j6i7h8g9f0e1d2,2022-12-27T20:06:38+00:00,2023-08-03T10:25:57+00:00,"read(\"any\")",Kathryn Henry,38 +c3b4a5z6y7x8w9v0,2020-04-20T04:41:24+00:00,2021-02-13T19:14:06+00:00,"read(\"any\")",Christopher Landry,23 +u1t2s3r4q5p6o7n8,2023-05-08T00:58:10+00:00,2024-07-05T03:36:48+00:00,"read(\"any\")",Jennifer Mcgee,62 +m9l0k1j2i3h4g5f6,2021-09-01T06:11:42+00:00,2022-01-26T16:59:23+00:00,"read(\"any\")",Cathy Church,35 +e7d8c9b0a1z2y3x4,2022-02-18T15:24:07+00:00,2023-04-12T00:40:31+00:00,"read(\"any\")",Jose Lopez,41 +w5v6u7t8s9r0q1p2,2020-12-13T09:03:55+00:00,2021-11-06T11:23:16+00:00,"read(\"any\")",William Rose,30 +o3n4m5l6k7j8i9h0,2021-12-13T09:03:55+00:00,2022-11-06T11:23:16+00:00,"read(\"any\")",Charles Hammer,61