From f2ea0b9b48a7976aacdfdfb756506a09b0b15aa5 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 1 Apr 2026 10:20:20 +0530 Subject: [PATCH 1/7] Fix PHPStan baseline cleanup issues (part 2) --- phpstan-baseline.neon | 508 ------------------ src/Appwrite/GraphQL/Schema.php | 151 +++--- src/Appwrite/GraphQL/Types.php | 23 +- src/Appwrite/GraphQL/Types/Mapper.php | 4 +- src/Appwrite/Migration/Migration.php | 25 + src/Appwrite/Migration/Version/V15.php | 9 +- src/Appwrite/Migration/Version/V20.php | 2 +- .../Http/Deployments/Download/Get.php | 15 +- .../Functions/Http/Functions/Update.php | 2 - .../Sites/Http/Deployments/Download/Get.php | 15 +- .../Modules/Sites/Http/Sites/Update.php | 2 - .../Modules/Storage/Http/Buckets/Update.php | 4 - .../Http/GitHub/Authorize/External/Update.php | 5 +- .../Modules/VCS/Http/GitHub/Callback/Get.php | 9 +- .../Modules/VCS/Http/GitHub/Deployment.php | 23 +- .../Modules/VCS/Http/GitHub/Events/Create.php | 2 +- src/Appwrite/SDK/Specification/Format.php | 3 - .../SDK/Specification/Format/OpenAPI3.php | 25 +- .../SDK/Specification/Format/Swagger2.php | 20 +- .../Legacy/DatabasesStringTypesTest.php | 8 +- .../Permissions/DatabasesPermissionsBase.php | 6 + .../Functions/FunctionsCustomClientTest.php | 2 +- .../Functions/FunctionsCustomServerTest.php | 1 + tests/e2e/Services/GraphQL/MessagingTest.php | 2 +- .../Services/GraphQL/StorageClientTest.php | 2 + .../Services/GraphQL/StorageServerTest.php | 2 + .../e2e/Services/Messaging/MessagingBase.php | 2 +- .../Services/Migrations/MigrationsBase.php | 22 +- tests/e2e/Services/Storage/StorageBase.php | 4 +- .../TablesDB/DatabasesStringTypesTest.php | 8 +- tests/e2e/Services/Users/UsersBase.php | 50 +- .../e2e/Services/VCS/VCSConsoleClientTest.php | 16 +- tests/unit/Auth/KeyTest.php | 6 +- 33 files changed, 236 insertions(+), 742 deletions(-) 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/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..cfd94d7c1e 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,8 @@ class Get extends Action } $state = \json_decode($state, true); + $redirectFailure = $state['failure'] ?? ''; + $redirectSuccess = $state['success'] ?? ''; $projectId = $state['projectId'] ?? ''; $project = $dbForPlatform->getDocument('projects', $projectId); @@ -74,10 +76,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); @@ -94,7 +97,6 @@ class Get extends Action $state = \array_merge($defaultState, $state ?? []); - $redirectSuccess = $state['success'] ?? ''; $redirectFailure = $state['failure'] ?? ''; // Create / Update installation @@ -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..83db6e76fb 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 ($additionalMethods !== null) { $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..da24b1e2b1 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 ($additionalMethods !== null) { $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..a1011f3b69 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 = []; } /** 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]), From 983adf3ffd3a2bfaa386e101334033b2691f41c3 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 1 Apr 2026 11:00:26 +0530 Subject: [PATCH 2/7] Fix analyze regressions in PHPStan cleanup --- src/Appwrite/Event/Message/Usage.php | 2 +- tests/e2e/Services/Migrations/MigrationsBase.php | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Appwrite/Event/Message/Usage.php b/src/Appwrite/Event/Message/Usage.php index c97b96a855..eb40f6dfea 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 diff --git a/tests/e2e/Services/Migrations/MigrationsBase.php b/tests/e2e/Services/Migrations/MigrationsBase.php index a1011f3b69..9e9ce2fbcd 100644 --- a/tests/e2e/Services/Migrations/MigrationsBase.php +++ b/tests/e2e/Services/Migrations/MigrationsBase.php @@ -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', From 1788e1bd6c3776edf24368c7aec06e1b7634147f Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 1 Apr 2026 11:15:59 +0530 Subject: [PATCH 3/7] Address PR review feedback --- src/Appwrite/Platform/Modules/VCS/Http/GitHub/Callback/Get.php | 2 +- src/Appwrite/SDK/Specification/Format/OpenAPI3.php | 2 +- src/Appwrite/SDK/Specification/Format/Swagger2.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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 cfd94d7c1e..69da270e19 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Callback/Get.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Callback/Get.php @@ -66,7 +66,6 @@ class Get extends Action $state = \json_decode($state, true); $redirectFailure = $state['failure'] ?? ''; - $redirectSuccess = $state['success'] ?? ''; $projectId = $state['projectId'] ?? ''; $project = $dbForPlatform->getDocument('projects', $projectId); @@ -97,6 +96,7 @@ class Get extends Action $state = \array_merge($defaultState, $state ?? []); + $redirectSuccess = $state['success'] ?? ''; $redirectFailure = $state['failure'] ?? ''; // Create / Update installation diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index 83db6e76fb..753a0dc52f 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -165,7 +165,7 @@ class OpenAPI3 extends Format ]; } - if ($additionalMethods !== null) { + if (\is_array($additionalMethods) && \count($additionalMethods) > 0) { $temp['x-appwrite']['methods'] = []; foreach ($additionalMethods as $methodObj) { /** @var Method $methodObj */ diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index da24b1e2b1..3e9ac891fa 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -172,7 +172,7 @@ class Swagger2 extends Format $temp['produces'][] = $produces; } - if ($additionalMethods !== null) { + if (\is_array($additionalMethods) && \count($additionalMethods) > 0) { $temp['x-appwrite']['methods'] = []; foreach ($additionalMethods as $methodObj) { /** @var Method $methodObj */ From 358f1b78a8012445f75d5150b455fd94325bab90 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 1 Apr 2026 12:00:32 +0530 Subject: [PATCH 4/7] Speed up PHPStan analysis with result caching Configure a project-local result cache directory so PHPStan only re-analyses files that changed. In CI, persist the cache across runs with actions/cache and suppress progress output. --- .github/workflows/ci.yml | 10 +++++++++- .gitignore | 1 + phpstan.neon | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa6dbe2bc3..5f1b9f8aeb 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: ./vendor/bin/phpstan analyse -c phpstan.neon --memory-limit=1G --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/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 From 44f33473bd62928dd9c712b0f3aeb84bb5fb0e3d Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 1 Apr 2026 12:04:31 +0530 Subject: [PATCH 5/7] Use composer analyze in CI to stay in sync with local workflow --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f1b9f8aeb..f44d5eedf3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -159,7 +159,7 @@ jobs: phpstan- - name: Run PHPStan - run: ./vendor/bin/phpstan analyse -c phpstan.neon --memory-limit=1G --no-progress + run: composer analyze -- --no-progress locale: name: Checks / Locale From 3cd90ae629f7f695e29f54f1bb09d209878b8ec8 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 1 Apr 2026 12:59:51 +0530 Subject: [PATCH 6/7] fix analyze --- src/Appwrite/Event/Message/Usage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Event/Message/Usage.php b/src/Appwrite/Event/Message/Usage.php index c97b96a855..776188d5b5 100644 --- a/src/Appwrite/Event/Message/Usage.php +++ b/src/Appwrite/Event/Message/Usage.php @@ -40,7 +40,7 @@ class Usage extends Base */ public static function fromArray(array $data): 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'] ?? []), From 2d34301834f564c821382192adcbe6c6f1913572 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Apr 2026 01:13:01 +0000 Subject: [PATCH 7/7] fix: add missing userType field to legacy log endpoints The users.php and messaging.php legacy controllers were missing the userType field in their log output, creating an inconsistency with the new audit user type distinction feature. Also adds missing mode field to users.php logs endpoint. https://claude.ai/code/session_01J9gKXwbHoLggsGwJi6KUnM --- app/controllers/api/messaging.php | 4 ++++ app/controllers/api/users.php | 2 ++ 2 files changed, 6 insertions(+) diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 1ba5eb1119..025e1d6aa1 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -1180,6 +1180,7 @@ Http::get('/v1/messaging/providers/:providerId/logs') 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, + 'userType' => $log['data']['userType'] ?? null, 'ip' => $log['ip'], 'time' => $log['time'], 'osCode' => $os['osCode'], @@ -2585,6 +2586,7 @@ Http::get('/v1/messaging/topics/:topicId/logs') 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, + 'userType' => $log['data']['userType'] ?? null, 'ip' => $log['ip'], 'time' => $log['time'], 'osCode' => $os['osCode'], @@ -3000,6 +3002,7 @@ Http::get('/v1/messaging/subscribers/:subscriberId/logs') 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, + 'userType' => $log['data']['userType'] ?? null, 'ip' => $log['ip'], 'time' => $log['time'], 'osCode' => $os['osCode'], @@ -3813,6 +3816,7 @@ Http::get('/v1/messaging/messages/:messageId/logs') 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, + 'userType' => $log['data']['userType'] ?? null, 'ip' => $log['ip'], 'time' => $log['time'], 'osCode' => $os['osCode'], diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 3b21d4797d..f0b15b4274 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -972,6 +972,8 @@ Http::get('/v1/users/:userId/logs') 'userId' => ID::custom($log['data']['userId']), 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, + 'mode' => $log['data']['mode'] ?? null, + 'userType' => $log['data']['userType'] ?? null, 'ip' => $log['ip'], 'time' => $log['time'], 'osCode' => $os['osCode'],