diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 682c18f85f..b984fe1574 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -150,8 +150,16 @@ jobs: - name: Install dependencies run: composer install --prefer-dist --no-progress --ignore-platform-reqs + - name: Cache PHPStan result cache + uses: actions/cache@v4 + with: + path: .phpstan-cache + key: phpstan-${{ github.sha }} + restore-keys: | + phpstan- + - name: Run PHPStan - run: composer analyze + run: composer analyze -- --no-progress locale: name: Checks / Locale diff --git a/.gitignore b/.gitignore index d6e138a382..6846e19aa7 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ appwrite.config.json /app/config/specs/ /docs/examples/ .phpunit.cache +.phpstan-cache playwright-report test-results docker-compose.web-installer.yml diff --git a/README.md b/README.md index 457863d236..9815229e43 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ Before running the installation command, make sure you have [Docker](https://www ```bash docker run -it --rm \ + --publish 20080:20080 \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ @@ -84,6 +85,7 @@ docker run -it --rm \ ```cmd docker run -it --rm ^ + --publish 20080:20080 ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ @@ -94,6 +96,7 @@ docker run -it --rm ^ ```powershell docker run -it --rm ` + --publish 20080:20080 ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` diff --git a/app/init/resources.php b/app/init/resources.php index ddfbb5c348..5ebb3ccded 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -888,7 +888,6 @@ Http::setResource('dbForPlatform', function (Group $pools, Cache $cache, Authori }, ['pools', 'cache', 'authorization']); Http::setResource('getDatabasesDB', function (Group $pools, Cache $cache, Document $project, Request $request, UsageContext $usage, Authorization $authorization) { - return function (Document $database) use ($pools, $cache, $project, $request, $usage, $authorization): Database { $databaseDSN = $database->getAttribute('database', $project->getAttribute('database', '')); $databaseType = $database->getAttribute('type', ''); @@ -907,11 +906,12 @@ Http::setResource('getDatabasesDB', function (Group $pools, Cache $cache, Docume $dsn = new DSN('mysql://' . $project->getAttribute('database')); } - $pool = $pools->get($databaseDSN->getHost()); + $databaseHost = $databaseDSN->getHost(); + $pool = $pools->get($databaseHost); $adapter = new DatabasePool($pool); $database = new Database($adapter, $cache); - $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); + $sharedTables = \array_filter(\explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''))); $database ->setDatabase(APP_DATABASE) @@ -922,7 +922,32 @@ Http::setResource('getDatabasesDB', function (Group $pools, Cache $cache, Docume ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); // inside pools authorization needs to be set first $database->getAdapter()->setSupportForAttributes($databaseType !== DOCUMENTSDB); - if (\in_array($dsn->getHost(), $sharedTables)) { + + // For separate pools (documentsdb/vectorsdb), check their own shared tables config + if ($databaseHost !== $dsn->getHost()) { + $dbTypeSharedTables = match ($databaseType) { + DOCUMENTSDB => \array_filter(\explode(',', System::getEnv('_APP_DATABASE_DOCUMENTSDB_SHARED_TABLES', ''))), + VECTORSDB => \array_filter(\explode(',', System::getEnv('_APP_DATABASE_VECTORSDB_SHARED_TABLES', ''))), + default => [], + }; + + if (\in_array($databaseHost, $dbTypeSharedTables)) { + $database + ->setSharedTables(true) + ->setTenant($project->getSequence()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getSequence()); + } + + try { + $database->create(); + } catch (\Utopia\Database\Exception\Duplicate) { + } + } elseif (\in_array($dsn->getHost(), $sharedTables)) { $database ->setSharedTables(true) ->setTenant($project->getSequence()) diff --git a/app/worker.php b/app/worker.php index e5aa32938f..b224696f5d 100644 --- a/app/worker.php +++ b/app/worker.php @@ -246,7 +246,8 @@ Server::setResource('getDatabasesDB', function (Cache $cache, Registry $register } $pools = $register->get('pools'); - $pool = $pools->get($databaseDSN->getHost()); + $databaseHost = $databaseDSN->getHost(); + $pool = $pools->get($databaseHost); $adapter = new DatabasePool($pool); $database = new Database($adapter, $cache); @@ -255,9 +256,33 @@ Server::setResource('getDatabasesDB', function (Cache $cache, Registry $register ->setAuthorization($authorization); $database->getAdapter()->setSupportForAttributes($databaseType !== DOCUMENTSDB); - $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); + $sharedTables = \array_filter(\explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''))); - if (\in_array($dsn->getHost(), $sharedTables, true)) { + // For separate pools (documentsdb/vectorsdb), check their own shared tables config + if ($databaseHost !== $dsn->getHost()) { + $dbTypeSharedTables = match ($databaseType) { + DOCUMENTSDB => \array_filter(\explode(',', System::getEnv('_APP_DATABASE_DOCUMENTSDB_SHARED_TABLES', ''))), + VECTORSDB => \array_filter(\explode(',', System::getEnv('_APP_DATABASE_VECTORSDB_SHARED_TABLES', ''))), + default => [], + }; + + if (\in_array($databaseHost, $dbTypeSharedTables)) { + $database + ->setSharedTables(true) + ->setTenant($projectDocument->getSequence()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $projectDocument->getSequence()); + } + + try { + $database->create(); + } catch (\Utopia\Database\Exception\Duplicate) { + } + } elseif (\in_array($dsn->getHost(), $sharedTables, true)) { $database ->setSharedTables(true) ->setTenant($projectDocument->getSequence()) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 5da64a1c97..8e9d8a5a38 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -216,102 +216,6 @@ parameters: count: 1 path: src/Appwrite/GraphQL/Resolvers.php - - - message: '#^Variable \$databaseId might not be defined\.$#' - identifier: variable.undefined - count: 5 - path: src/Appwrite/GraphQL/Schema.php - - - - message: '#^Variable \$sdk in empty\(\) always exists and is not falsy\.$#' - identifier: empty.variable - count: 1 - path: src/Appwrite/GraphQL/Schema.php - - - - message: '#^Method Appwrite\\GraphQL\\Types\:\:assoc\(\) should return Appwrite\\GraphQL\\Types\\Json but returns GraphQL\\Type\\Definition\\Type\.$#' - identifier: return.type - count: 1 - path: src/Appwrite/GraphQL/Types.php - - - - message: '#^Method Appwrite\\GraphQL\\Types\:\:inputFile\(\) should return Appwrite\\GraphQL\\Types\\InputFile but returns GraphQL\\Type\\Definition\\Type\.$#' - identifier: return.type - count: 1 - path: src/Appwrite/GraphQL/Types.php - - - - message: '#^Method Appwrite\\GraphQL\\Types\:\:json\(\) should return Appwrite\\GraphQL\\Types\\Json but returns GraphQL\\Type\\Definition\\Type\.$#' - identifier: return.type - count: 1 - path: src/Appwrite/GraphQL/Types.php - - - - message: '#^Class Appwrite\\Network\\Validator\\CNAME not found\.$#' - identifier: class.notFound - count: 1 - path: src/Appwrite/GraphQL/Types/Mapper.php - - - - message: '#^Class Utopia\\Validator\\Origin not found\.$#' - identifier: class.notFound - count: 1 - path: src/Appwrite/GraphQL/Types/Mapper.php - - - - message: '#^Call to an undefined method Appwrite\\Migration\\Version\\V15\:\:documentsIterator\(\)\.$#' - identifier: method.notFound - count: 7 - path: src/Appwrite/Migration/Version/V15.php - - - - message: '#^Method Appwrite\\Migration\\Version\\V15\:\:fixDocument\(\) should return Utopia\\Database\\Document but empty return statement found\.$#' - identifier: return.empty - count: 1 - path: src/Appwrite/Migration/Version/V15.php - - - - message: '#^PHPDoc tag @return with type string\|false is not subtype of native type string\.$#' - identifier: return.phpDocType - count: 1 - path: src/Appwrite/Migration/Version/V15.php - - - - message: '#^Variable \$tag on left side of \?\? always exists and is always null\.$#' - identifier: nullCoalesce.variable - count: 1 - path: src/Appwrite/Migration/Version/V15.php - - - - message: '#^Call to an undefined method Appwrite\\Migration\\Version\\V17\:\:documentsIterator\(\)\.$#' - identifier: method.notFound - count: 1 - path: src/Appwrite/Migration/Version/V17.php - - - - message: '#^Call to an undefined method Appwrite\\Migration\\Version\\V18\:\:documentsIterator\(\)\.$#' - identifier: method.notFound - count: 2 - path: src/Appwrite/Migration/Version/V18.php - - - - message: '#^Call to an undefined method Appwrite\\Migration\\Version\\V19\:\:documentsIterator\(\)\.$#' - identifier: method.notFound - count: 4 - path: src/Appwrite/Migration/Version/V19.php - - - - message: '#^Call to an undefined method Appwrite\\Migration\\Version\\V20\:\:documentsIterator\(\)\.$#' - identifier: method.notFound - count: 6 - path: src/Appwrite/Migration/Version/V20.php - - - - message: '#^Variable \$query on left side of \?\? always exists and is not nullable\.$#' - identifier: nullCoalesce.variable - count: 1 - path: src/Appwrite/Migration/Version/V20.php - - message: '#^Method Appwrite\\Network\\Cors\:\:headers\(\) should return array\ but returns array\\.$#' identifier: return.type @@ -474,18 +378,6 @@ parameters: count: 1 path: src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Logs/XList.php - - - message: '#^Variable \$device might not be defined\.$#' - identifier: variable.undefined - count: 5 - path: src/Appwrite/Platform/Modules/Functions/Http/Deployments/Download/Get.php - - - - message: '#^Variable \$path might not be defined\.$#' - identifier: variable.undefined - count: 5 - path: src/Appwrite/Platform/Modules/Functions/Http/Deployments/Download/Get.php - - message: '#^Call to method getAttribute\(\) on an unknown class Appwrite\\Platform\\Modules\\Functions\\Http\\Executions\\Utopia\\Database\\Document\.$#' identifier: class.notFound @@ -546,12 +438,6 @@ parameters: count: 1 path: src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php - - - message: '#^Variable \$enabled on left side of \?\?\= always exists and is not nullable\.$#' - identifier: nullCoalesce.variable - count: 1 - path: src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php - - message: '#^Undefined variable\: \$cpus$#' identifier: variable.undefined @@ -600,24 +486,6 @@ parameters: count: 1 path: src/Appwrite/Platform/Modules/Proxy/Http/Rules/Verification/Update.php - - - message: '#^Variable \$device might not be defined\.$#' - identifier: variable.undefined - count: 5 - path: src/Appwrite/Platform/Modules/Sites/Http/Deployments/Download/Get.php - - - - message: '#^Variable \$path might not be defined\.$#' - identifier: variable.undefined - count: 5 - path: src/Appwrite/Platform/Modules/Sites/Http/Deployments/Download/Get.php - - - - message: '#^Variable \$enabled on left side of \?\?\= always exists and is not nullable\.$#' - identifier: nullCoalesce.variable - count: 1 - path: src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php - - message: '#^Variable \$iv might not be defined\.$#' identifier: variable.undefined @@ -630,30 +498,6 @@ parameters: count: 1 path: src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php - - - message: '#^Variable \$allowedFileExtensions on left side of \?\?\= always exists and is not nullable\.$#' - identifier: nullCoalesce.variable - count: 1 - path: src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php - - - - message: '#^Variable \$antivirus on left side of \?\?\= always exists and is not nullable\.$#' - identifier: nullCoalesce.variable - count: 1 - path: src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php - - - - message: '#^Variable \$enabled on left side of \?\?\= always exists and is not nullable\.$#' - identifier: nullCoalesce.variable - count: 1 - path: src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php - - - - message: '#^Variable \$transformations on left side of \?\?\= always exists and is not nullable\.$#' - identifier: nullCoalesce.variable - count: 1 - path: src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php - - message: '#^Caught class Appwrite\\Platform\\Modules\\Teams\\Http\\Memberships\\Throwable not found\.$#' identifier: class.notFound @@ -678,78 +522,6 @@ parameters: count: 14 path: src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php - - - message: '#^Variable \$logBase might not be defined\.$#' - identifier: variable.undefined - count: 1 - path: src/Appwrite/Platform/Modules/VCS/Http/GitHub/Authorize/External/Update.php - - - - message: '#^Variable \$previewUrl in empty\(\) always exists and is not falsy\.$#' - identifier: empty.variable - count: 1 - path: src/Appwrite/Platform/Modules/VCS/Http/GitHub/Authorize/External/Update.php - - - - message: '#^Variable \$repositoryName in empty\(\) always exists and is not falsy\.$#' - identifier: empty.variable - count: 1 - path: src/Appwrite/Platform/Modules/VCS/Http/GitHub/Authorize/External/Update.php - - - - message: '#^Variable \$rule in empty\(\) always exists and is not falsy\.$#' - identifier: empty.variable - count: 1 - path: src/Appwrite/Platform/Modules/VCS/Http/GitHub/Authorize/External/Update.php - - - - message: '#^Result of method Utopia\\Http\\Response\:\:redirect\(\) \(void\) is used\.$#' - identifier: method.void - count: 2 - path: src/Appwrite/Platform/Modules/VCS/Http/GitHub/Callback/Get.php - - - - message: '#^Undefined variable\: \$redirectFailure$#' - identifier: variable.undefined - count: 2 - path: src/Appwrite/Platform/Modules/VCS/Http/GitHub/Callback/Get.php - - - - message: '#^Variable \$redirectFailure in empty\(\) is never defined\.$#' - identifier: empty.variable - count: 1 - path: src/Appwrite/Platform/Modules/VCS/Http/GitHub/Callback/Get.php - - - - message: '#^Result of method Appwrite\\Utopia\\Response\:\:json\(\) \(void\) is used\.$#' - identifier: method.void - count: 1 - path: src/Appwrite/Platform/Modules/VCS/Http/GitHub/Events/Create.php - - - - message: '#^Variable \$logBase might not be defined\.$#' - identifier: variable.undefined - count: 1 - path: src/Appwrite/Platform/Modules/VCS/Http/GitHub/Events/Create.php - - - - message: '#^Variable \$previewUrl in empty\(\) always exists and is not falsy\.$#' - identifier: empty.variable - count: 1 - path: src/Appwrite/Platform/Modules/VCS/Http/GitHub/Events/Create.php - - - - message: '#^Variable \$repositoryName in empty\(\) always exists and is not falsy\.$#' - identifier: empty.variable - count: 1 - path: src/Appwrite/Platform/Modules/VCS/Http/GitHub/Events/Create.php - - - - message: '#^Variable \$rule in empty\(\) always exists and is not falsy\.$#' - identifier: empty.variable - count: 1 - path: src/Appwrite/Platform/Modules/VCS/Http/GitHub/Events/Create.php - - message: '#^Variable \$providerConfig on left side of \?\? always exists and is not nullable\.$#' identifier: nullCoalesce.variable @@ -918,108 +690,6 @@ parameters: count: 1 path: src/Appwrite/SDK/Method.php - - - message: '#^PHPDoc tag @param references unknown parameter\: \$services$#' - identifier: parameter.notFound - count: 1 - path: src/Appwrite/SDK/Specification/Format.php - - - - message: '#^PHPDoc tag @return with type Appwrite\\SDK\\Specification\\Format is incompatible with native type array\.$#' - identifier: return.phpDocType - count: 1 - path: src/Appwrite/SDK/Specification/Format.php - - - - message: '#^Cannot unset offset ''schema'' on array\{description\: ''No content'', content\?\: non\-empty\-array\<''''\|''\*/\*''\|''application/json''\|''image/\*''\|''image/png''\|''multipart/form\-data''\|''text/html''\|''text/plain'', array\{schema\: array\{''\$ref''\: non\-falsy\-string\}\}\|array\{schema\: array\{oneOf\: array\\}\}\>\}\.$#' - identifier: unset.offset - count: 1 - path: src/Appwrite/SDK/Specification/Format/OpenAPI3.php - - - - message: '#^Class Utopia\\Database\\Validator\\DatetimeValidator not found\.$#' - identifier: class.notFound - count: 1 - path: src/Appwrite/SDK/Specification/Format/OpenAPI3.php - - - - message: '#^Class Utopia\\Validator\\Length not found\.$#' - identifier: class.notFound - count: 1 - path: src/Appwrite/SDK/Specification/Format/OpenAPI3.php - - - - message: '#^Class Utopia\\Validator\\Mock not found\.$#' - identifier: class.notFound - count: 1 - path: src/Appwrite/SDK/Specification/Format/OpenAPI3.php - - - - message: '#^Offset ''securityDefinitions'' on array\{openapi\: ''3\.0\.0'', info\: array\{version\: string, title\: string, description\: string, termsOfService\: string, contact\: array\{name\: string, url\: string, email\: string\}, license\: array\{name\: ''BSD\-3\-Clause'', url\: ''https\://raw…''\}\}, servers\: array\{array\{url\: string\}, array\{url\: string\}\}, paths\: array\{\}, tags\: array, components\: array\{schemas\: array\{\}, securitySchemes\: array\}, externalDocs\: array\{description\: string, url\: string\}\} in isset\(\) does not exist\.$#' - identifier: isset.offset - count: 1 - path: src/Appwrite/SDK/Specification/Format/OpenAPI3.php - - - - message: '#^Variable \$desc on left side of \?\?\= always exists and is not nullable\.$#' - identifier: nullCoalesce.variable - count: 1 - path: src/Appwrite/SDK/Specification/Format/OpenAPI3.php - - - - message: '#^Variable \$validator in empty\(\) always exists and is not falsy\.$#' - identifier: empty.variable - count: 1 - path: src/Appwrite/SDK/Specification/Format/OpenAPI3.php - - - - message: '#^Class Utopia\\Database\\Validator\\DatetimeValidator not found\.$#' - identifier: class.notFound - count: 1 - path: src/Appwrite/SDK/Specification/Format/Swagger2.php - - - - message: '#^Class Utopia\\Validator\\Length not found\.$#' - identifier: class.notFound - count: 1 - path: src/Appwrite/SDK/Specification/Format/Swagger2.php - - - - message: '#^Class Utopia\\Validator\\Mock not found\.$#' - identifier: class.notFound - count: 1 - path: src/Appwrite/SDK/Specification/Format/Swagger2.php - - - - message: '#^PHPDoc tag @var with type Appwrite\\SDK\\Method is not subtype of native type \*NEVER\*\.$#' - identifier: varTag.nativeType - count: 1 - path: src/Appwrite/SDK/Specification/Format/Swagger2.php - - - - message: '#^Variable \$additionalMethods in empty\(\) always exists and is always falsy\.$#' - identifier: empty.variable - count: 1 - path: src/Appwrite/SDK/Specification/Format/Swagger2.php - - - - message: '#^Variable \$desc on left side of \?\?\= always exists and is not nullable\.$#' - identifier: nullCoalesce.variable - count: 1 - path: src/Appwrite/SDK/Specification/Format/Swagger2.php - - - - message: '#^Variable \$sdk in empty\(\) always exists and is not falsy\.$#' - identifier: empty.variable - count: 1 - path: src/Appwrite/SDK/Specification/Format/Swagger2.php - - - - message: '#^Variable \$validator in empty\(\) always exists and is not falsy\.$#' - identifier: empty.variable - count: 1 - path: src/Appwrite/SDK/Specification/Format/Swagger2.php - - message: '#^PHPDoc tag @param has invalid value \(Document \$this\)\: Unexpected token "\$this", expected variable at offset 69 on line 4$#' identifier: phpDoc.parseError @@ -1032,196 +702,18 @@ parameters: count: 1 path: src/Appwrite/Utopia/Database/Documents/User.php - - - message: '#^Unsafe access to private property Tests\\E2E\\Services\\Databases\\Legacy\\DatabasesStringTypesTest\:\:\$setupCache through static\:\:\.$#' - identifier: staticClassAccess.privateProperty - count: 4 - path: tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php - - - message: '#^Call to an undefined method Tests\\E2E\\Services\\Databases\\Permissions\\LegacyPermissionsGuestTest\:\:getIndexUrl\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/e2e/Services/Databases/Permissions/LegacyPermissionsGuestTest.php - - - - message: '#^Call to an undefined method Tests\\E2E\\Services\\Databases\\Permissions\\LegacyPermissionsMemberTest\:\:getIndexUrl\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/e2e/Services/Databases/Permissions/LegacyPermissionsMemberTest.php - - - - message: '#^Call to an undefined method Tests\\E2E\\Services\\Databases\\Permissions\\LegacyPermissionsTeamTest\:\:getIndexUrl\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/e2e/Services/Databases/Permissions/LegacyPermissionsTeamTest.php - - - - message: '#^Method PHPUnit\\Framework\\TestCase\:\:addToAssertionCount\(\) invoked with 2 parameters, 1 required\.$#' - identifier: arguments.count - count: 1 - path: tests/e2e/Services/Functions/FunctionsCustomClientTest.php - - - - message: '#^Variable \$largeTag might not be defined\.$#' - identifier: variable.undefined - count: 8 - path: tests/e2e/Services/Functions/FunctionsCustomServerTest.php - - message: '#^Binary operation "\+" between string and 1 results in an error\.$#' identifier: binaryOp.invalid count: 1 path: tests/e2e/Services/GraphQL/Legacy/AbuseTest.php - - - message: '#^Variable \$from in empty\(\) is never defined\.$#' - identifier: empty.variable - count: 1 - path: tests/e2e/Services/GraphQL/MessagingTest.php - - - - message: '#^Method Tests\\E2E\\Services\\GraphQL\\StorageClientTest\:\:testGetFileDownload\(\) should return array but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: tests/e2e/Services/GraphQL/StorageClientTest.php - - - - message: '#^Method Tests\\E2E\\Services\\GraphQL\\StorageServerTest\:\:testGetFileDownload\(\) should return array but return statement is missing\.$#' - identifier: return.missing - count: 1 - path: tests/e2e/Services/GraphQL/StorageServerTest.php - - message: '#^Binary operation "\+" between string and 1 results in an error\.$#' identifier: binaryOp.invalid count: 1 path: tests/e2e/Services/GraphQL/TablesDB/AbuseTest.php - - - message: '#^Variable \$from in empty\(\) is never defined\.$#' - identifier: empty.variable - count: 1 - path: tests/e2e/Services/Messaging/MessagingConsoleClientTest.php - - - - message: '#^Variable \$from in empty\(\) is never defined\.$#' - identifier: empty.variable - count: 1 - path: tests/e2e/Services/Messaging/MessagingCustomClientTest.php - - - - message: '#^Variable \$from in empty\(\) is never defined\.$#' - identifier: empty.variable - count: 1 - path: tests/e2e/Services/Messaging/MessagingCustomServerTest.php - - - - message: '#^Anonymous function has an unused use \$databaseId\.$#' - identifier: closure.unusedUse - count: 6 - path: tests/e2e/Services/Migrations/MigrationsConsoleClientTest.php - - - - message: '#^Anonymous function has an unused use \$tableId\.$#' - identifier: closure.unusedUse - count: 6 - path: tests/e2e/Services/Migrations/MigrationsConsoleClientTest.php - - - - message: '#^Variable \$largeFile might not be defined\.$#' - identifier: variable.undefined - count: 8 - path: tests/e2e/Services/Storage/StorageConsoleClientTest.php - - - - message: '#^Variable \$largeFile might not be defined\.$#' - identifier: variable.undefined - count: 8 - path: tests/e2e/Services/Storage/StorageCustomClientTest.php - - - - message: '#^Variable \$largeFile might not be defined\.$#' - identifier: variable.undefined - count: 8 - path: tests/e2e/Services/Storage/StorageCustomServerTest.php - - - - message: '#^Unsafe access to private property Tests\\E2E\\Services\\TablesDB\\DatabasesStringTypesTest\:\:\$setupCache through static\:\:\.$#' - identifier: staticClassAccess.privateProperty - count: 4 - path: tests/e2e/Services/TablesDB/DatabasesStringTypesTest.php - - - - message: '#^Call to an undefined method Tests\\E2E\\Services\\TablesDB\\Permissions\\TablesDBPermissionsGuestTest\:\:getIndexUrl\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/e2e/Services/TablesDB/Permissions/TablesDBPermissionsGuestTest.php - - - - message: '#^Call to an undefined method Tests\\E2E\\Services\\TablesDB\\Permissions\\TablesDBPermissionsMemberTest\:\:getIndexUrl\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/e2e/Services/TablesDB/Permissions/TablesDBPermissionsMemberTest.php - - - - message: '#^Call to an undefined method Tests\\E2E\\Services\\TablesDB\\Permissions\\TablesDBPermissionsTeamTest\:\:getIndexUrl\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/e2e/Services/TablesDB/Permissions/TablesDBPermissionsTeamTest.php - - - message: '#^Unsafe access to private property Tests\\E2E\\Services\\Users\\UsersCustomServerTest\:\:\$cachedHashedPasswordUsers through static\:\:\.$#' - identifier: staticClassAccess.privateProperty - count: 2 - path: tests/e2e/Services/Users/UsersCustomServerTest.php - - - - message: '#^Unsafe access to private property Tests\\E2E\\Services\\Users\\UsersCustomServerTest\:\:\$cachedUser through static\:\:\.$#' - identifier: staticClassAccess.privateProperty - count: 7 - path: tests/e2e/Services/Users/UsersCustomServerTest.php - - - - message: '#^Unsafe access to private property Tests\\E2E\\Services\\Users\\UsersCustomServerTest\:\:\$cachedUserTarget through static\:\:\.$#' - identifier: staticClassAccess.privateProperty - count: 7 - path: tests/e2e/Services/Users/UsersCustomServerTest.php - - - - message: '#^Unsafe access to private property Tests\\E2E\\Services\\Users\\UsersCustomServerTest\:\:\$userEmailUpdated through static\:\:\.$#' - identifier: staticClassAccess.privateProperty - count: 3 - path: tests/e2e/Services/Users/UsersCustomServerTest.php - - - - message: '#^Unsafe access to private property Tests\\E2E\\Services\\Users\\UsersCustomServerTest\:\:\$userNameUpdated through static\:\:\.$#' - identifier: staticClassAccess.privateProperty - count: 3 - path: tests/e2e/Services/Users/UsersCustomServerTest.php - - - - message: '#^Unsafe access to private property Tests\\E2E\\Services\\Users\\UsersCustomServerTest\:\:\$userNumberUpdated through static\:\:\.$#' - identifier: staticClassAccess.privateProperty - count: 3 - path: tests/e2e/Services/Users/UsersCustomServerTest.php - - - - message: '#^Unsafe access to private property Tests\\E2E\\Services\\VCS\\VCSConsoleClientTest\:\:\$cachedFunctionData through static\:\:\.$#' - identifier: staticClassAccess.privateProperty - count: 4 - path: tests/e2e/Services/VCS/VCSConsoleClientTest.php - - - - message: '#^Unsafe access to private property Tests\\E2E\\Services\\VCS\\VCSConsoleClientTest\:\:\$cachedInstallationId through static\:\:\.$#' - identifier: staticClassAccess.privateProperty - count: 4 - path: tests/e2e/Services/VCS/VCSConsoleClientTest.php - - - - message: '#^Unsafe call to private method Tests\\Unit\\Auth\\KeyTest\:\:generateKey\(\) through static\:\:\.$#' - identifier: staticClassAccess.privateMethod - count: 3 - path: tests/unit/Auth/KeyTest.php - - message: '#^Call to an undefined method Utopia\\Queue\\Publisher\:\:getEvents\(\)\.$#' identifier: method.notFound diff --git a/phpstan.neon b/phpstan.neon index b87ad46eca..25fe377ecf 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,6 +3,7 @@ includes: parameters: level: 3 + tmpDir: .phpstan-cache paths: - src - app diff --git a/src/Appwrite/Event/Message/Usage.php b/src/Appwrite/Event/Message/Usage.php index 316404d0f2..ec8484e45e 100644 --- a/src/Appwrite/Event/Message/Usage.php +++ b/src/Appwrite/Event/Message/Usage.php @@ -4,7 +4,7 @@ namespace Appwrite\Event\Message; use Utopia\Database\Document; -class Usage extends Base +final class Usage extends Base { /** * @param Document $project @@ -40,8 +40,7 @@ class Usage extends Base */ public static function fromArray(array $data): static { - /** @phpstan-ignore new.static */ - return new static( + return new self( project: new Document($data['project'] ?? []), metrics: $data['metrics'] ?? [], reduce: array_map(fn (array $doc) => new Document($doc), $data['reduce'] ?? []), diff --git a/src/Appwrite/GraphQL/Schema.php b/src/Appwrite/GraphQL/Schema.php index 57115ff027..aa68fd28a1 100644 --- a/src/Appwrite/GraphQL/Schema.php +++ b/src/Appwrite/GraphQL/Schema.php @@ -98,10 +98,9 @@ class Schema foreach ($routes as $route) { /** @var Route $route */ - /** @var \Appwrite\SDK\Method $sdk */ $sdk = $route->getLabel('sdk', false); - if (empty($sdk)) { + if ($sdk === false) { continue; } @@ -177,7 +176,7 @@ class Schema $required = $attr['required']; $default = $attr['default']; $escapedKey = str_replace('$', '', $key); - $collections[$collectionId][$escapedKey] = [ + $collections[$databaseId][$collectionId][$escapedKey] = [ 'type' => Mapper::attribute( $type, $array, @@ -187,80 +186,82 @@ class Schema ]; } - foreach ($collections as $collectionId => $attributes) { - $objectType = new ObjectType([ - 'name' => $collectionId, - 'fields' => \array_merge( - ["_id" => ['type' => Type::string()]], - $attributes - ), - ]); - $attributes = \array_merge( - $attributes, - Mapper::args('mutate') - ); - - $queryFields[$collectionId . 'Get'] = [ - 'type' => $objectType, - 'args' => Mapper::args('id'), - 'resolve' => Resolvers::documentGet( - $utopia, - $databaseId, - $collectionId, - $urls['get'], - ) - ]; - $queryFields[$collectionId . 'List'] = [ - 'type' => Type::listOf($objectType), - 'args' => Mapper::args('list'), - 'resolve' => Resolvers::documentList( - $utopia, - $databaseId, - $collectionId, - $urls['list'], - $params['list'], - ), - 'complexity' => $complexity, - ]; - - $mutationFields[$collectionId . 'Create'] = [ - 'type' => $objectType, - 'args' => $attributes, - 'resolve' => Resolvers::documentCreate( - $utopia, - $databaseId, - $collectionId, - $urls['create'], - $params['create'], - ) - ]; - $mutationFields[$collectionId . 'Update'] = [ - 'type' => $objectType, - 'args' => \array_merge( - Mapper::args('id'), - \array_map( - fn ($attr) => $attr['type'] = Type::getNullableType($attr['type']), + foreach ($collections as $databaseId => $databaseCollections) { + foreach ($databaseCollections as $collectionId => $attributes) { + $objectType = new ObjectType([ + 'name' => $collectionId, + 'fields' => \array_merge( + ["_id" => ['type' => Type::string()]], $attributes + ), + ]); + $attributes = \array_merge( + $attributes, + Mapper::args('mutate') + ); + + $queryFields[$collectionId . 'Get'] = [ + 'type' => $objectType, + 'args' => Mapper::args('id'), + 'resolve' => Resolvers::documentGet( + $utopia, + $databaseId, + $collectionId, + $urls['get'], ) - ), - 'resolve' => Resolvers::documentUpdate( - $utopia, - $databaseId, - $collectionId, - $urls['update'], - $params['update'], - ) - ]; - $mutationFields[$collectionId . 'Delete'] = [ - 'type' => Mapper::model('none'), - 'args' => Mapper::args('id'), - 'resolve' => Resolvers::documentDelete( - $utopia, - $databaseId, - $collectionId, - $urls['delete'], - ) - ]; + ]; + $queryFields[$collectionId . 'List'] = [ + 'type' => Type::listOf($objectType), + 'args' => Mapper::args('list'), + 'resolve' => Resolvers::documentList( + $utopia, + $databaseId, + $collectionId, + $urls['list'], + $params['list'], + ), + 'complexity' => $complexity, + ]; + + $mutationFields[$collectionId . 'Create'] = [ + 'type' => $objectType, + 'args' => $attributes, + 'resolve' => Resolvers::documentCreate( + $utopia, + $databaseId, + $collectionId, + $urls['create'], + $params['create'], + ) + ]; + $mutationFields[$collectionId . 'Update'] = [ + 'type' => $objectType, + 'args' => \array_merge( + Mapper::args('id'), + \array_map( + fn ($attr) => $attr['type'] = Type::getNullableType($attr['type']), + $attributes + ) + ), + 'resolve' => Resolvers::documentUpdate( + $utopia, + $databaseId, + $collectionId, + $urls['update'], + $params['update'], + ) + ]; + $mutationFields[$collectionId . 'Delete'] = [ + 'type' => Mapper::model('none'), + 'args' => Mapper::args('id'), + 'resolve' => Resolvers::documentDelete( + $utopia, + $databaseId, + $collectionId, + $urls['delete'], + ) + ]; + } } $offset += $limit; } diff --git a/src/Appwrite/GraphQL/Types.php b/src/Appwrite/GraphQL/Types.php index 279cac2068..3d5979dc18 100644 --- a/src/Appwrite/GraphQL/Types.php +++ b/src/Appwrite/GraphQL/Types.php @@ -15,10 +15,13 @@ class Types * * @return Json */ - public static function json(): Type + public static function json(): Json { if (Registry::has(Json::class)) { - return Registry::get(Json::class); + $type = Registry::get(Json::class); + if ($type instanceof Json) { + return $type; + } } $type = new Json(); Registry::set(Json::class, $type); @@ -28,12 +31,15 @@ class Types /** * Get the JSON type. * - * @return Json + * @return Assoc */ - public static function assoc(): Type + public static function assoc(): Assoc { if (Registry::has(Assoc::class)) { - return Registry::get(Assoc::class); + $type = Registry::get(Assoc::class); + if ($type instanceof Assoc) { + return $type; + } } $type = new Assoc(); Registry::set(Assoc::class, $type); @@ -45,10 +51,13 @@ class Types * * @return InputFile */ - public static function inputFile(): Type + public static function inputFile(): InputFile { if (Registry::has(InputFile::class)) { - return Registry::get(InputFile::class); + $type = Registry::get(InputFile::class); + if ($type instanceof InputFile) { + return $type; + } } $type = new InputFile(); Registry::set(InputFile::class, $type); diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index de4913cec4..53474b855a 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -273,11 +273,9 @@ class Mapper case \Appwrite\Auth\Validator\Password::class: case \Appwrite\Event\Validator\Event::class: case \Appwrite\Event\Validator\FunctionEvent::class: - case \Appwrite\Network\Validator\CNAME::class: case \Utopia\Emails\Validator\Email::class: case \Appwrite\Network\Validator\Redirect::class: case \Appwrite\Network\Validator\DNS::class: - case \Appwrite\Network\Validator\Origin::class: case \Appwrite\Task\Validator\Cron::class: case \Appwrite\Utopia\Database\Validator\CustomId::class: case \Utopia\Database\Validator\Key::class: @@ -286,7 +284,7 @@ class Mapper case \Utopia\Validator\HexColor::class: case \Utopia\Validator\Host::class: case \Utopia\Validator\IP::class: - case \Utopia\Validator\Origin::class: + case \Appwrite\Network\Validator\Origin::class: case \Utopia\Validator\Text::class: case \Utopia\Validator\URL::class: case \Utopia\Validator\WhiteList::class: diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index a4f73eb5f2..e481eebf6e 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -13,6 +13,7 @@ use Utopia\Database\Exception\Limit; use Utopia\Database\Exception\Structure; use Utopia\Database\Helpers\ID; use Utopia\Database\PDO; +use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; abstract class Migration @@ -204,6 +205,30 @@ abstract class Migration } } + /** + * @param array $queries + * @return \Generator + * @throws Exception + */ + protected function documentsIterator(string $collection, array $queries = []): \Generator + { + $offset = 0; + + do { + $documents = $this->dbForProject->find($collection, [ + ...$queries, + Query::limit($this->limit), + Query::offset($offset), + ]); + + foreach ($documents as $document) { + yield $document; + } + + $offset += \count($documents); + } while (\count($documents) === $this->limit); + } + /** * Creates collection from the config collection. * diff --git a/src/Appwrite/Migration/Version/V15.php b/src/Appwrite/Migration/Version/V15.php index 66037660c0..eefa84ec22 100644 --- a/src/Appwrite/Migration/Version/V15.php +++ b/src/Appwrite/Migration/Version/V15.php @@ -1224,7 +1224,7 @@ class V15 extends Migration * @param \Utopia\Database\Document $document * @return \Utopia\Database\Document */ - protected function fixDocument(Document $document) + protected function fixDocument(Document $document): Document { switch ($document->getCollection()) { case 'cache': @@ -1234,7 +1234,7 @@ class V15 extends Migration * skipping migration for 'cache' and 'variables'. * 'users' already migrated. */ - return; + return $document; case '_metadata': /** @@ -1480,7 +1480,6 @@ class V15 extends Migration * Filter from the 'encrypt' filter. * * @param string $value - * @return string|false */ protected function encryptFilter(string $value): string { @@ -1492,8 +1491,8 @@ class V15 extends Migration 'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag), 'method' => OpenSSL::CIPHER_AES_128_GCM, 'iv' => \bin2hex($iv), - 'tag' => \bin2hex($tag ?? ''), + 'tag' => \bin2hex($tag), 'version' => '1', - ]); + ]) ?: ''; } } diff --git a/src/Appwrite/Migration/Version/V20.php b/src/Appwrite/Migration/Version/V20.php index 3c13815949..e3458c815e 100644 --- a/src/Appwrite/Migration/Version/V20.php +++ b/src/Appwrite/Migration/Version/V20.php @@ -452,7 +452,7 @@ class V20 extends Migration Query::equal('period', ['1d']), ]); - $value = $query ?? 0; + $value = $query; $this->createInfMetric($to, $value); } 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 e9c665ea4b..50c901e4c8 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Download/Get.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Download/Get.php @@ -88,16 +88,11 @@ class Get extends Action throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } - switch ($type) { - case 'output': - $path = $deployment->getAttribute('buildPath', ''); - $device = $deviceForBuilds; - break; - case 'source': - $path = $deployment->getAttribute('sourcePath', ''); - $device = $deviceForFunctions; - break; - } + [$path, $device] = match ($type) { + 'output' => [$deployment->getAttribute('buildPath', ''), $deviceForBuilds], + 'source' => [$deployment->getAttribute('sourcePath', ''), $deviceForFunctions], + default => throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid deployment download type.'), + }; if (!$device->exists($path)) { throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php index a627bae9dd..71fc99a30e 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php @@ -170,8 +170,6 @@ class Update extends Base $runtime = $function->getAttribute('runtime'); } - $enabled ??= $function->getAttribute('enabled', true); - $repositoryId = $function->getAttribute('repositoryId', ''); $repositoryInternalId = $function->getAttribute('repositoryInternalId', ''); 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 600135e152..339d059365 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Download/Get.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Download/Get.php @@ -87,16 +87,11 @@ class Get extends Action throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } - switch ($type) { - case 'output': - $path = $deployment->getAttribute('buildPath', ''); - $device = $deviceForBuilds; - break; - case 'source': - $path = $deployment->getAttribute('sourcePath', ''); - $device = $deviceForSites; - break; - } + [$path, $device] = match ($type) { + 'output' => [$deployment->getAttribute('buildPath', ''), $deviceForBuilds], + 'source' => [$deployment->getAttribute('sourcePath', ''), $deviceForSites], + default => throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid deployment download type.'), + }; if (!$device->exists($path)) { throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php index 6510e505ae..dd9bedffb5 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php @@ -172,8 +172,6 @@ class Update extends Base $framework = $site->getAttribute('framework'); } - $enabled ??= $site->getAttribute('enabled', true); - $repositoryId = $site->getAttribute('repositoryId', ''); $repositoryInternalId = $site->getAttribute('repositoryInternalId', ''); diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php index e4827a6354..406f8e4f49 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php @@ -99,12 +99,8 @@ class Update extends Action $permissions ??= $bucket->getPermissions(); $maximumFileSize ??= $bucket->getAttribute('maximumFileSize', (int) System::getEnv('_APP_STORAGE_LIMIT', 0)); - $allowedFileExtensions ??= $bucket->getAttribute('allowedFileExtensions', []); - $enabled ??= $bucket->getAttribute('enabled', true); $encryption ??= $bucket->getAttribute('encryption', true); - $antivirus ??= $bucket->getAttribute('antivirus', true); $compression ??= $bucket->getAttribute('compression', Compression::NONE); - $transformations ??= $bucket->getAttribute('transformations', true); // Map aggregate permissions into the multiple permissions they represent. $permissions = Permission::aggregate($permissions); diff --git a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Authorize/External/Update.php b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Authorize/External/Update.php index 4a34ffd36a..8b320535e9 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Authorize/External/Update.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Authorize/External/Update.php @@ -110,10 +110,7 @@ class Update extends Action $providerRepositoryId = $repository->getAttribute('providerRepositoryId'); try { - $providerRepositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; - if (empty($providerRepositoryName)) { - throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); - } + $providerRepositoryName = $github->getRepositoryName($providerRepositoryId); } catch (RepositoryNotFound $e) { throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Callback/Get.php b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Callback/Get.php index 914bcaa93e..69da270e19 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Callback/Get.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Callback/Get.php @@ -65,6 +65,7 @@ class Get extends Action } $state = \json_decode($state, true); + $redirectFailure = $state['failure'] ?? ''; $projectId = $state['projectId'] ?? ''; $project = $dbForPlatform->getDocument('projects', $projectId); @@ -74,10 +75,11 @@ class Get extends Action if (!empty($redirectFailure)) { $separator = \str_contains($redirectFailure, '?') ? '&' : ':'; - return $response + $response ->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') ->addHeader('Pragma', 'no-cache') ->redirect($redirectFailure . $separator . \http_build_query(['error' => $error])); + return; } throw new Exception(Exception::PROJECT_NOT_FOUND, $error); @@ -165,10 +167,11 @@ class Get extends Action if (!empty($redirectFailure)) { $separator = \str_contains($redirectFailure, '?') ? '&' : ':'; - return $response + $response ->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') ->addHeader('Pragma', 'no-cache') ->redirect($redirectFailure . $separator . \http_build_query(['error' => $error])); + return; } throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $error); diff --git a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php index ae730c3f74..6e1db12c28 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php @@ -49,6 +49,8 @@ trait Deployment ) { $errors = []; foreach ($repositories as $repository) { + $logBase = 'vcs.github.event.repo.unknown'; + try { $repositoryId = $repository->getId(); $projectId = $repository->getAttribute('projectId'); @@ -107,18 +109,11 @@ trait Deployment $owner = $github->getOwnerName($providerInstallationId) ?? ''; try { - $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; - if (empty($repositoryName)) { - throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); - } + $repositoryName = $github->getRepositoryName($providerRepositoryId); } catch (RepositoryNotFound $e) { throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); } - if (empty($repositoryName)) { - throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); - } - $isAuthorized = !$external; if (!$isAuthorized && !empty($providerPullRequestId)) { @@ -291,10 +286,7 @@ trait Deployment $providerRepositoryId = $repository->getAttribute('providerRepositoryId'); try { - $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; - if (empty($repositoryName)) { - throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); - } + $repositoryName = $github->getRepositoryName($providerRepositoryId); } catch (RepositoryNotFound $e) { throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); } @@ -501,7 +493,7 @@ trait Deployment $rule = $authorization->skip(fn () => $dbForPlatform->getDocument('rules', $previewRuleId)); $protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https'; - $previewUrl = !empty($rule) ? ("{$protocol}://" . $rule->getAttribute('domain', '')) : ''; + $previewUrl = !$rule->isEmpty() ? ("{$protocol}://" . $rule->getAttribute('domain', '')) : ''; if (!empty($previewUrl)) { $comment = new Comment($platform); @@ -524,10 +516,7 @@ trait Deployment $providerRepositoryId = $repository->getAttribute('providerRepositoryId'); try { - $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; - if (empty($repositoryName)) { - throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); - } + $repositoryName = $github->getRepositoryName($providerRepositoryId); } catch (RepositoryNotFound $e) { throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); } diff --git a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Events/Create.php b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Events/Create.php index a2fa44c613..e3dbcfa0e9 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Events/Create.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Events/Create.php @@ -83,7 +83,7 @@ class Create extends Action default => null, }; - return $response->json($parsedPayload); + $response->json($parsedPayload); } protected function preprocessEvent(Request $request) diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index 04ecafa8fc..7a867c5b91 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -204,9 +204,6 @@ abstract class Format * * Get services value * - * @param array $services - * - * @return self */ public function getServices(): array { diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index 8c77da413f..753a0dc52f 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -79,8 +79,8 @@ class OpenAPI3 extends Format $output['components']['securitySchemes']['Key']['x-appwrite'] = ['demo' => '']; } - if (isset($output['securityDefinitions']['JWT'])) { - $output['securityDefinitions']['JWT']['x-appwrite'] = ['demo' => '']; + if (isset($output['components']['securitySchemes']['JWT'])) { + $output['components']['securitySchemes']['JWT']['x-appwrite'] = ['demo' => '']; } if (isset($output['components']['securitySchemes']['Locale'])) { @@ -99,7 +99,7 @@ class OpenAPI3 extends Format $sdk = $route->getLabel('sdk', false); - if (empty($sdk)) { + if ($sdk === false) { continue; } @@ -125,7 +125,9 @@ class OpenAPI3 extends Format $namespace = $sdk->getNamespace() ?? 'default'; - $desc ??= ''; + if ($desc === null) { + $desc = ''; + } $descContents = \str_ends_with($desc, '.md') ? \file_get_contents($desc) : $desc; $temp = [ @@ -163,7 +165,7 @@ class OpenAPI3 extends Format ]; } - if (!empty($additionalMethods)) { + if (\is_array($additionalMethods) && \count($additionalMethods) > 0) { $temp['x-appwrite']['methods'] = []; foreach ($additionalMethods as $methodObj) { /** @var Method $methodObj */ @@ -329,7 +331,7 @@ class OpenAPI3 extends Format if (($response->getCode() ?? 500) === 204) { $temp['responses'][(string)$response->getCode() ?? '500']['description'] = 'No content'; - unset($temp['responses'][(string)$response->getCode() ?? '500']['schema']); + unset($temp['responses'][(string)$response->getCode() ?? '500']['content']); } } @@ -383,7 +385,7 @@ class OpenAPI3 extends Format $validator = $validator->getValidator(); } - $class = !empty($validator) + $class = $validator instanceof Validator ? \get_class($validator) : ''; @@ -431,7 +433,7 @@ class OpenAPI3 extends Format $node['schema']['type'] = $validator->getType(); $node['schema']['x-example'] = ($param['example'] ?? '') ?: '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; break; - case \Utopia\Database\Validator\DatetimeValidator::class: + case \Utopia\Database\Validator\Datetime::class: $node['schema']['type'] = $validator->getType(); $node['schema']['format'] = 'datetime'; $node['schema']['x-example'] = ($param['example'] ?? '') ?: Model::TYPE_DATETIME_EXAMPLE; @@ -463,7 +465,6 @@ class OpenAPI3 extends Format $node['schema']['x-example'] = ($param['example'] ?? '') ?: 'https://example.com'; break; case \Utopia\Validator\JSON::class: - case \Utopia\Validator\Mock::class: case \Utopia\Validator\Assoc::class: $param['default'] = (empty($param['default'])) ? new \stdClass() : $param['default']; $node['schema']['type'] = 'object'; @@ -563,12 +564,6 @@ class OpenAPI3 extends Format $node['schema']['x-example'] = $param['example']; } break; - case \Utopia\Validator\Length::class: - $node['schema']['type'] = $validator->getType(); - if (!empty($param['example'])) { - $node['schema']['x-example'] = $param['example']; - } - break; case \Utopia\Validator\WhiteList::class: if ($array) { $validator = $validator->getValidator(); diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index d0815d8cad..3e9ac891fa 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -96,10 +96,9 @@ class Swagger2 extends Format $scope = $route->getLabel('scope', ''); - /** @var Method $sdk */ $sdk = $route->getLabel('sdk', false); - if (empty($sdk)) { + if ($sdk === false) { continue; } @@ -127,7 +126,9 @@ class Swagger2 extends Format $sdkPlatforms = array_values(array_unique($sdkPlatforms)); $namespace = $sdk->getNamespace() ?? 'default'; - $desc ??= ''; + if ($desc === null) { + $desc = ''; + } $descContents = \str_ends_with($desc, '.md') ? \file_get_contents($desc) : $desc; $temp = [ @@ -171,7 +172,7 @@ class Swagger2 extends Format $temp['produces'][] = $produces; } - if (!empty($additionalMethods)) { + if (\is_array($additionalMethods) && \count($additionalMethods) > 0) { $temp['x-appwrite']['methods'] = []; foreach ($additionalMethods as $methodObj) { /** @var Method $methodObj */ @@ -388,7 +389,7 @@ class Swagger2 extends Format $validator = $validator->getValidator(); } - $class = !empty($validator) + $class = $validator instanceof Validator ? \get_class($validator) : ''; @@ -436,7 +437,7 @@ class Swagger2 extends Format $node['type'] = $validator->getType(); $node['x-example'] = ($param['example'] ?? '') ?: '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; break; - case \Utopia\Database\Validator\DatetimeValidator::class: + case \Utopia\Database\Validator\Datetime::class: $node['type'] = $validator->getType(); $node['format'] = 'datetime'; $node['x-example'] = ($param['example'] ?? '') ?: Model::TYPE_DATETIME_EXAMPLE; @@ -479,7 +480,6 @@ class Swagger2 extends Format } break; case \Utopia\Validator\JSON::class: - case \Utopia\Validator\Mock::class: case \Utopia\Validator\Assoc::class: $node['type'] = 'object'; $node['default'] = (empty($param['default'])) ? new \stdClass() : $param['default']; @@ -552,12 +552,6 @@ class Swagger2 extends Format $node['x-example'] = $param['example']; } break; - case \Utopia\Validator\Length::class: - $node['type'] = $validator->getType(); - if (!empty($param['example'])) { - $node['x-example'] = $param['example']; - } - break; case \Utopia\Validator\WhiteList::class: if ($array) { $validator = $validator->getValidator(); diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php b/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php index f68f0b909e..06c008e61d 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesStringTypesTest.php @@ -29,8 +29,8 @@ class DatabasesStringTypesTest extends Scope protected function setupDatabaseAndCollection(): array { $cacheKey = $this->getProject()['$id'] ?? 'default'; - if (!empty(static::$setupCache[$cacheKey])) { - return static::$setupCache[$cacheKey]; + if (!empty(self::$setupCache[$cacheKey])) { + return self::$setupCache[$cacheKey]; } $projectId = $this->getProject()['$id']; @@ -135,12 +135,12 @@ class DatabasesStringTypesTest extends Scope // Wait for all attributes to be available $this->waitForAllAttributes($databaseId, $collectionId); - static::$setupCache[$cacheKey] = [ + self::$setupCache[$cacheKey] = [ 'databaseId' => $databaseId, 'collectionId' => $collectionId, ]; - return static::$setupCache[$cacheKey]; + return self::$setupCache[$cacheKey]; } public function testCreateDatabase(): void diff --git a/tests/e2e/Services/Databases/Permissions/DatabasesPermissionsBase.php b/tests/e2e/Services/Databases/Permissions/DatabasesPermissionsBase.php index d6869cc650..134257d06a 100644 --- a/tests/e2e/Services/Databases/Permissions/DatabasesPermissionsBase.php +++ b/tests/e2e/Services/Databases/Permissions/DatabasesPermissionsBase.php @@ -45,6 +45,12 @@ trait DatabasesPermissionsBase return $recordId ? "{$base}/{$recordId}" : $base; } + protected function getIndexUrl(string $databaseId, string $containerId, string $indexId = ''): string + { + $base = "{$this->getContainerUrl($databaseId, $containerId)}/indexes"; + return $indexId ? "{$base}/{$indexId}" : $base; + } + // User Management Methods public function createUser(string $id, string $email, string $password = 'test123!'): array { diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 1d61cb0ebb..1208121077 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -414,7 +414,7 @@ class FunctionsCustomClientTest extends Scope 'offset' => 2 ]); $this->assertEquals(200, $templatesOffset['headers']['status-code']); - $this->addToAssertionCount(1, $templatesOffset['body']['templates']); + $this->addToAssertionCount(1); $this->assertEquals($templates['body']['templates'][2]['id'], $templatesOffset['body']['templates'][0]['id']); // List templates with filters diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index d0b2190f1c..ba518ee0b6 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -1003,6 +1003,7 @@ class FunctionsCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'] ]; $id = ''; + $largeTag = null; while (!feof($handle)) { $curlFile = new \CURLFile('data://' . $mimeType . ';base64,' . base64_encode(@fread($handle, $chunkSize)), $mimeType, 'large-fx.tar.gz'); $headers['content-range'] = 'bytes ' . ($counter * $chunkSize) . '-' . min(((($counter * $chunkSize) + $chunkSize) - 1), $size - 1) . '/' . $size; diff --git a/tests/e2e/Services/GraphQL/MessagingTest.php b/tests/e2e/Services/GraphQL/MessagingTest.php index 03e7cc00f6..c76b9a2e4d 100644 --- a/tests/e2e/Services/GraphQL/MessagingTest.php +++ b/tests/e2e/Services/GraphQL/MessagingTest.php @@ -409,7 +409,7 @@ class MessagingTest extends Scope $apiKey = $emailDSN->getPassword(); $domain = $emailDSN->getUser(); - if (empty($to) || empty($from) || empty($apiKey) || empty($domain) || empty($isEuRegion)) { + if (empty($to) || empty($fromName) || empty($fromEmail) || empty($apiKey) || empty($domain) || empty($isEuRegion)) { $this->markTestSkipped('Email provider not configured'); } diff --git a/tests/e2e/Services/GraphQL/StorageClientTest.php b/tests/e2e/Services/GraphQL/StorageClientTest.php index 3e02de0585..25041e843b 100644 --- a/tests/e2e/Services/GraphQL/StorageClientTest.php +++ b/tests/e2e/Services/GraphQL/StorageClientTest.php @@ -229,6 +229,8 @@ class StorageClientTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertEquals(47218, \strlen($file['body'])); + + return $file; } /** diff --git a/tests/e2e/Services/GraphQL/StorageServerTest.php b/tests/e2e/Services/GraphQL/StorageServerTest.php index 9622582e80..cc4c8ecec3 100644 --- a/tests/e2e/Services/GraphQL/StorageServerTest.php +++ b/tests/e2e/Services/GraphQL/StorageServerTest.php @@ -291,6 +291,8 @@ class StorageServerTest extends Scope ], $this->getHeaders()), $gqlPayload); $this->assertEquals(47218, \strlen($file['body'])); + + return $file; } /** diff --git a/tests/e2e/Services/Messaging/MessagingBase.php b/tests/e2e/Services/Messaging/MessagingBase.php index 160ea61568..d83b450739 100644 --- a/tests/e2e/Services/Messaging/MessagingBase.php +++ b/tests/e2e/Services/Messaging/MessagingBase.php @@ -2241,7 +2241,7 @@ trait MessagingBase $authKey = $smsDSN->getPassword(); $templateId = $smsDSN->getParam('templateId'); - if (empty($to) || empty($from) || empty($senderId) || empty($authKey)) { + if (empty($to) || empty($senderId) || empty($authKey)) { $this->markTestSkipped('SMS provider not configured'); } diff --git a/tests/e2e/Services/Migrations/MigrationsBase.php b/tests/e2e/Services/Migrations/MigrationsBase.php index 6b446145fe..9e9ce2fbcd 100644 --- a/tests/e2e/Services/Migrations/MigrationsBase.php +++ b/tests/e2e/Services/Migrations/MigrationsBase.php @@ -64,8 +64,8 @@ trait MigrationsBase */ protected function setupMigrationDatabase(): array { - if (!empty(static::$cachedDatabaseData)) { - return static::$cachedDatabaseData; + if (!empty(self::$cachedDatabaseData)) { + return self::$cachedDatabaseData; } $response = $this->client->call(Client::METHOD_POST, '/databases', [ @@ -81,11 +81,11 @@ trait MigrationsBase $this->assertNotEmpty($response['body']); $this->assertNotEmpty($response['body']['$id']); - static::$cachedDatabaseData = [ + self::$cachedDatabaseData = [ 'databaseId' => $response['body']['$id'], ]; - return static::$cachedDatabaseData; + return self::$cachedDatabaseData; } /** @@ -94,8 +94,8 @@ trait MigrationsBase */ protected function setupMigrationTable(): array { - if (!empty(static::$cachedTableData)) { - return static::$cachedTableData; + if (!empty(self::$cachedTableData)) { + return self::$cachedTableData; } // Ensure database exists first @@ -141,12 +141,12 @@ trait MigrationsBase $this->assertEquals('available', $response['body']['status']); }, 5000, 500); - static::$cachedTableData = [ + self::$cachedTableData = [ 'databaseId' => $databaseId, 'tableId' => $tableId, ]; - return static::$cachedTableData; + return self::$cachedTableData; } public function performMigrationSync(array $body): array @@ -670,7 +670,7 @@ trait MigrationsBase ]); // Clear the cache since we cleaned up - static::$cachedDatabaseData = []; + self::$cachedDatabaseData = []; } public function testAppwriteMigrationDatabasesRow(): void @@ -757,8 +757,8 @@ trait MigrationsBase ]); // Clear the caches since we cleaned up - static::$cachedDatabaseData = []; - static::$cachedTableData = []; + self::$cachedDatabaseData = []; + self::$cachedTableData = []; } /** @@ -1331,7 +1331,7 @@ trait MigrationsBase ] ); - $this->assertEventually(function () use ($missingColumn, $databaseId, $tableId) { + $this->assertEventually(function () use ($missingColumn) { $migrationId = $missingColumn['body']['$id']; $migration = $this->client->call(Client::METHOD_GET, '/migrations/'.$migrationId, array_merge([ 'content-type' => 'application/json', @@ -1363,7 +1363,7 @@ trait MigrationsBase ] ); - $this->assertEventually(function () use ($missingColumn, $databaseId, $tableId) { + $this->assertEventually(function () use ($missingColumn) { $migrationId = $missingColumn['body']['$id']; $migration = $this->client->call(Client::METHOD_GET, '/migrations/'.$migrationId, array_merge([ 'content-type' => 'application/json', @@ -1395,7 +1395,7 @@ trait MigrationsBase ] ); - $this->assertEventually(function () use ($irrelevantColumn, $databaseId, $tableId) { + $this->assertEventually(function () use ($irrelevantColumn) { $migrationId = $irrelevantColumn['body']['$id']; $migration = $this->client->call(Client::METHOD_GET, '/migrations/'.$migrationId, array_merge([ 'content-type' => 'application/json', @@ -1422,7 +1422,7 @@ trait MigrationsBase ] ); - $this->assertEventually(function () use ($migration, $databaseId, $tableId) { + $this->assertEventually(function () use ($migration) { $migrationId = $migration['body']['$id']; $migration = $this->client->call(Client::METHOD_GET, '/migrations/'.$migrationId, array_merge([ 'content-type' => 'application/json', @@ -1464,7 +1464,7 @@ trait MigrationsBase ] ); - $this->assertEventually(function () use ($migration, $databaseId, $tableId) { + $this->assertEventually(function () use ($migration) { $migrationId = $migration['body']['$id']; $migration = $this->client->call(Client::METHOD_GET, '/migrations/'.$migrationId, array_merge([ 'content-type' => 'application/json', @@ -4018,7 +4018,7 @@ trait MigrationsBase ] ); - $this->assertEventually(function () use ($migration, $databaseId, $tableId) { + $this->assertEventually(function () use ($migration) { $migrationId = $migration['body']['$id']; $migration = $this->client->call(Client::METHOD_GET, '/migrations/'.$migrationId, array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index 03723bf231..21365e49f1 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -3045,7 +3045,11 @@ class RealtimeCustomClientTest extends Scope $event = null; $deadline = \time() + 10; while (\time() < $deadline) { - $raw = $client->receive(); + try { + $raw = $client->receive(); + } catch (\WebSocket\ConnectionException) { + break; + } $msg = json_decode($raw, true); if (($msg['type'] ?? '') === 'event' && \in_array($updateEvent, $msg['data']['events'] ?? [])) { $event = $msg; diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index 9f4105e1cb..e5bb06cc48 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -97,6 +97,7 @@ trait StorageBase 'x-appwrite-project' => $this->getProject()['$id'] ]; $id = ''; + $largeFile = null; while (!feof($handle)) { $curlFile = new \CURLFile('data://' . $mimeType . ';base64,' . base64_encode(@fread($handle, $chunkSize)), $mimeType, 'large-file.mp4'); $headers['content-range'] = 'bytes ' . ($counter * $chunkSize) . '-' . min(((($counter * $chunkSize) + $chunkSize) - 1), $size - 1) . '/' . $size; @@ -132,7 +133,7 @@ trait StorageBase self::$cachedBucketFile[$cacheKey] = [ 'bucketId' => $bucketId, 'fileId' => $file['body']['$id'], - 'largeFileId' => $largeFile['body']['$id'], + 'largeFileId' => $largeFile['body']['$id'] ?? '', 'largeBucketId' => $bucket2['body']['$id'], 'webpFileId' => $webpFile['body']['$id'] ]; @@ -261,6 +262,7 @@ trait StorageBase 'x-appwrite-project' => $this->getProject()['$id'] ]; $id = ''; + $largeFile = null; while (!feof($handle)) { $curlFile = new \CURLFile('data://' . $mimeType . ';base64,' . base64_encode(@fread($handle, $chunkSize)), $mimeType, 'large-file.mp4'); $headers['content-range'] = 'bytes ' . ($counter * $chunkSize) . '-' . min(((($counter * $chunkSize) + $chunkSize) - 1), $size - 1) . '/' . $size; diff --git a/tests/e2e/Services/TablesDB/DatabasesStringTypesTest.php b/tests/e2e/Services/TablesDB/DatabasesStringTypesTest.php index 44af63fb22..b7fd982a6d 100644 --- a/tests/e2e/Services/TablesDB/DatabasesStringTypesTest.php +++ b/tests/e2e/Services/TablesDB/DatabasesStringTypesTest.php @@ -29,8 +29,8 @@ class DatabasesStringTypesTest extends Scope protected function setupDatabaseAndTable(): array { $cacheKey = $this->getProject()['$id'] ?? 'default'; - if (!empty(static::$setupCache[$cacheKey])) { - return static::$setupCache[$cacheKey]; + if (!empty(self::$setupCache[$cacheKey])) { + return self::$setupCache[$cacheKey]; } $projectId = $this->getProject()['$id']; @@ -131,7 +131,7 @@ class DatabasesStringTypesTest extends Scope // Cache before waiting so that if waitForAllAttributes times out, // subsequent calls don't try to re-create the same columns (causing 409) - static::$setupCache[$cacheKey] = [ + self::$setupCache[$cacheKey] = [ 'databaseId' => $databaseId, 'tableId' => $tableId, ]; @@ -139,7 +139,7 @@ class DatabasesStringTypesTest extends Scope // Wait for all columns to be available $this->waitForAllAttributes($databaseId, $tableId); - return static::$setupCache[$cacheKey]; + return self::$setupCache[$cacheKey]; } public function testCreateDatabase(): void diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php index 940746a8eb..3255d9a67f 100644 --- a/tests/e2e/Services/Users/UsersBase.php +++ b/tests/e2e/Services/Users/UsersBase.php @@ -28,8 +28,8 @@ trait UsersBase protected function setupUser(): array { $projectId = $this->getProject()['$id']; - if (!empty(static::$cachedUser[$projectId])) { - return static::$cachedUser[$projectId]; + if (!empty(self::$cachedUser[$projectId])) { + return self::$cachedUser[$projectId]; } $user = $this->client->call(Client::METHOD_POST, '/users', array_merge([ @@ -52,16 +52,16 @@ trait UsersBase ]); if (!empty($response['body']['users'])) { - static::$cachedUser[$projectId] = ['userId' => $response['body']['users'][0]['$id']]; - return static::$cachedUser[$projectId]; + self::$cachedUser[$projectId] = ['userId' => $response['body']['users'][0]['$id']]; + return self::$cachedUser[$projectId]; } } if ($user['headers']['status-code'] === 201) { - static::$cachedUser[$projectId] = ['userId' => $user['body']['$id']]; + self::$cachedUser[$projectId] = ['userId' => $user['body']['$id']]; } - return static::$cachedUser[$projectId]; + return self::$cachedUser[$projectId]; } /** @@ -90,7 +90,7 @@ trait UsersBase protected function setupHashedPasswordUsers(): void { $projectId = $this->getProject()['$id']; - if (!empty(static::$cachedHashedPasswordUsers[$projectId])) { + if (!empty(self::$cachedHashedPasswordUsers[$projectId])) { return; } @@ -180,7 +180,7 @@ trait UsersBase 'passwordSignerKey' => 'XyEKE9RcTDeLEsL/RjwPDBv/RqDl8fb3gpYEOQaPihbxf1ZAtSOHCjuAAa7Q3oHpCYhXSN9tizHgVOwn6krflQ==', ]); - static::$cachedHashedPasswordUsers[$projectId] = true; + self::$cachedHashedPasswordUsers[$projectId] = true; } /** @@ -189,8 +189,8 @@ trait UsersBase protected function setupUserTarget(): array { $projectId = $this->getProject()['$id']; - if (!empty(static::$cachedUserTarget[$projectId])) { - return static::$cachedUserTarget[$projectId]; + if (!empty(self::$cachedUserTarget[$projectId])) { + return self::$cachedUserTarget[$projectId]; } $data = $this->setupUser(); @@ -233,10 +233,10 @@ trait UsersBase ]); if ($response['headers']['status-code'] === 201) { - static::$cachedUserTarget[$projectId] = $response['body']; + self::$cachedUserTarget[$projectId] = $response['body']; } - return static::$cachedUserTarget[$projectId] ?? []; + return self::$cachedUserTarget[$projectId] ?? []; } /** @@ -247,7 +247,7 @@ trait UsersBase $data = $this->setupUser(); $projectId = $this->getProject()['$id']; - if (static::$userNameUpdated) { + if (self::$userNameUpdated) { return $data; } @@ -258,7 +258,7 @@ trait UsersBase 'name' => 'Updated name', ]); - static::$userNameUpdated = true; + self::$userNameUpdated = true; return $data; } @@ -270,7 +270,7 @@ trait UsersBase $data = $this->setupUser(); $projectId = $this->getProject()['$id']; - if (static::$userEmailUpdated) { + if (self::$userEmailUpdated) { return $data; } @@ -281,7 +281,7 @@ trait UsersBase 'email' => 'users.service@updated.com', ]); - static::$userEmailUpdated = true; + self::$userEmailUpdated = true; return $data; } @@ -293,7 +293,7 @@ trait UsersBase $data = $this->setupUser(); $projectId = $this->getProject()['$id']; - if (static::$userNumberUpdated) { + if (self::$userNumberUpdated) { return $data; } @@ -304,7 +304,7 @@ trait UsersBase 'number' => '+910000000000', ]); - static::$userNumberUpdated = true; + self::$userNumberUpdated = true; return $data; } @@ -474,7 +474,7 @@ trait UsersBase // Cache the user ID for other tests $projectId = $this->getProject()['$id']; - static::$cachedUser[$projectId] = ['userId' => $body['$id']]; + self::$cachedUser[$projectId] = ['userId' => $body['$id']]; } /** @@ -1274,7 +1274,7 @@ trait UsersBase $this->assertEquals($user['body']['name'], 'Updated name'); // Mark name as updated for search tests - static::$userNameUpdated = true; + self::$userNameUpdated = true; } public function testUpdateUserNameSearch(): void @@ -1357,7 +1357,7 @@ trait UsersBase $this->assertEquals($user['body']['email'], 'users.service@updated.com'); // Mark email as updated for search tests - static::$userEmailUpdated = true; + self::$userEmailUpdated = true; } public function testUpdateUserEmailSearch(): void @@ -1645,7 +1645,7 @@ trait UsersBase $this->assertEquals($response['body']['type'], $errorType); // Mark phone as updated for search tests - static::$userNumberUpdated = true; + self::$userNumberUpdated = true; } public function testUpdateTwoUsersPhoneToEmpty(): void @@ -1954,7 +1954,7 @@ trait UsersBase // Cache for other tests $projectId = $this->getProject()['$id']; - static::$cachedUserTarget[$projectId] = $response['body']; + self::$cachedUserTarget[$projectId] = $response['body']; } public function testUpdateUserTarget(): void @@ -1973,7 +1973,7 @@ trait UsersBase // Update cache with new data $projectId = $this->getProject()['$id']; - static::$cachedUserTarget[$projectId] = $response['body']; + self::$cachedUserTarget[$projectId] = $response['body']; } public function testListUserTarget(): void @@ -2014,7 +2014,7 @@ trait UsersBase // Clear cached target since it was deleted $projectId = $this->getProject()['$id']; - unset(static::$cachedUserTarget[$projectId]); + unset(self::$cachedUserTarget[$projectId]); $response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/targets', array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/VCS/VCSConsoleClientTest.php b/tests/e2e/Services/VCS/VCSConsoleClientTest.php index b1b7e9a42a..854e7110f1 100644 --- a/tests/e2e/Services/VCS/VCSConsoleClientTest.php +++ b/tests/e2e/Services/VCS/VCSConsoleClientTest.php @@ -37,8 +37,8 @@ class VCSConsoleClientTest extends Scope { $projectId = $this->getProject()['$id']; - if (!empty(static::$cachedInstallationId[$projectId])) { - return static::$cachedInstallationId[$projectId]; + if (!empty(self::$cachedInstallationId[$projectId])) { + return self::$cachedInstallationId[$projectId]; } $response = $this->client->call(Client::METHOD_GET, '/mock/github/callback', array_merge([ @@ -48,8 +48,8 @@ class VCSConsoleClientTest extends Scope 'projectId' => $projectId, ]); - static::$cachedInstallationId[$projectId] = $response['body']['installationId']; - return static::$cachedInstallationId[$projectId]; + self::$cachedInstallationId[$projectId] = $response['body']['installationId']; + return self::$cachedInstallationId[$projectId]; } /** @@ -60,8 +60,8 @@ class VCSConsoleClientTest extends Scope { $projectId = $this->getProject()['$id']; - if (!empty(static::$cachedFunctionData[$projectId])) { - return static::$cachedFunctionData[$projectId]; + if (!empty(self::$cachedFunctionData[$projectId])) { + return self::$cachedFunctionData[$projectId]; } $installationId = $this->setupInstallation(); @@ -86,12 +86,12 @@ class VCSConsoleClientTest extends Scope 'providerBranch' => 'main', ]); - static::$cachedFunctionData[$projectId] = [ + self::$cachedFunctionData[$projectId] = [ 'installationId' => $installationId, 'functionId' => $function['body']['$id'] ]; - return static::$cachedFunctionData[$projectId]; + return self::$cachedFunctionData[$projectId]; } public function testGitHubAuthorize(): void diff --git a/tests/unit/Auth/KeyTest.php b/tests/unit/Auth/KeyTest.php index fc1779efad..58fe3113e1 100644 --- a/tests/unit/Auth/KeyTest.php +++ b/tests/unit/Auth/KeyTest.php @@ -25,7 +25,7 @@ class KeyTest extends TestCase $roleScopes = Config::getParam('roles', [])[User::ROLE_APPS]['scopes']; $guestRoleScopes = Config::getParam('roles', [])[User::ROLE_GUESTS]['scopes']; - $key = static::generateKey($projectId, $usage, $scopes); + $key = self::generateKey($projectId, $usage, $scopes); $decoded = Key::decode( project: new Document(['$id' => $projectId]), team: new Document(), @@ -50,7 +50,7 @@ class KeyTest extends TestCase 'previewAuthDisabled' => true, 'deploymentStatusIgnored' => true, ]; - $key = static::generateKey($projectId, $usage, $scopes, extra: $extra); + $key = self::generateKey($projectId, $usage, $scopes, extra: $extra); $decoded = Key::decode( project: new Document(['$id' => $projectId]), team: new Document(), @@ -88,7 +88,7 @@ class KeyTest extends TestCase $this->assertEquals('UNKNOWN', $decoded->getName()); // Decode expired dynamic key - $expiredKey = static::generateKey($projectId, $usage, $scopes, maxAge: 1, timestamp: time() - 60); + $expiredKey = self::generateKey($projectId, $usage, $scopes, maxAge: 1, timestamp: time() - 60); \sleep(2); $decoded = Key::decode( project: new Document(['$id' => $projectId]),