diff --git a/app/config/errors.php b/app/config/errors.php index a9ca0f79dd..bf0f4461f6 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -630,6 +630,11 @@ return [ 'description' => 'Site with the requested ID could not be found.', 'code' => 404, ], + Exception::SITE_ALREADY_EXISTS => [ + 'name' => Exception::SITE_ALREADY_EXISTS, + 'description' => 'Site with the requested ID already exists. Try again with a different ID or use ID.unique() to generate a unique ID.', + 'code' => 409, + ], Exception::SITE_TEMPLATE_NOT_FOUND => [ 'name' => Exception::SITE_TEMPLATE_NOT_FOUND, 'description' => 'Site Template with the requested ID could not be found.', @@ -1291,6 +1296,11 @@ return [ 'description' => 'Message with the requested ID could not be found.', 'code' => 404, ], + Exception::MESSAGE_ALREADY_EXISTS => [ + 'name' => Exception::MESSAGE_ALREADY_EXISTS, + 'description' => 'Message with the requested ID already exists. Try again with a different ID or use ID.unique() to generate a unique ID.', + 'code' => 409, + ], Exception::MESSAGE_MISSING_TARGET => [ 'name' => Exception::MESSAGE_MISSING_TARGET, 'description' => 'Message with the requested ID has no recipients (topics or users or targets).', diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index d0049c1397..a52ec70b12 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -3251,7 +3251,7 @@ Http::post('/v1/messaging/messages/email') } } - $message = $dbForProject->createDocument('messages', new Document([ + $message = new Document([ '$id' => $messageId, 'providerType' => MESSAGE_TYPE_EMAIL, 'topics' => $topics, @@ -3267,7 +3267,12 @@ Http::post('/v1/messaging/messages/email') 'attachments' => $attachments, ], 'status' => $status, - ])); + ]); + try { + $message = $dbForProject->createDocument('messages', $message); + } catch (DuplicateException) { + throw new Exception(Exception::MESSAGE_ALREADY_EXISTS); + } switch ($status) { case MessageStatus::PROCESSING: @@ -3400,7 +3405,7 @@ Http::post('/v1/messaging/messages/sms') } } - $message = $dbForProject->createDocument('messages', new Document([ + $message = new Document([ '$id' => $messageId, 'providerType' => MESSAGE_TYPE_SMS, 'topics' => $topics, @@ -3410,7 +3415,12 @@ Http::post('/v1/messaging/messages/sms') 'content' => $content, ], 'status' => $status, - ])); + ]); + try { + $message = $dbForProject->createDocument('messages', $message); + } catch (DuplicateException) { + throw new Exception(Exception::MESSAGE_ALREADY_EXISTS); + } switch ($status) { case MessageStatus::PROCESSING: @@ -3620,7 +3630,7 @@ Http::post('/v1/messaging/messages/push') $pushData['priority'] = $priority; } - $message = $dbForProject->createDocument('messages', new Document([ + $message = new Document([ '$id' => $messageId, 'providerType' => MESSAGE_TYPE_PUSH, 'topics' => $topics, @@ -3629,7 +3639,12 @@ Http::post('/v1/messaging/messages/push') 'scheduledAt' => $scheduledAt, 'data' => $pushData, 'status' => $status, - ])); + ]); + try { + $message = $dbForProject->createDocument('messages', $message); + } catch (DuplicateException) { + throw new Exception(Exception::MESSAGE_ALREADY_EXISTS); + } switch ($status) { case MessageStatus::PROCESSING: diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index e911faf77e..95a9c6ddac 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -166,6 +166,7 @@ class Exception extends \Exception /** Sites */ public const string SITE_NOT_FOUND = 'site_not_found'; + public const string SITE_ALREADY_EXISTS = 'site_already_exists'; public const string SITE_TEMPLATE_NOT_FOUND = 'site_template_not_found'; /** Functions */ @@ -365,6 +366,7 @@ class Exception extends \Exception /** Message */ public const string MESSAGE_NOT_FOUND = 'message_not_found'; + public const string MESSAGE_ALREADY_EXISTS = 'message_already_exists'; public const string MESSAGE_MISSING_TARGET = 'message_missing_target'; public const string MESSAGE_ALREADY_SENT = 'message_already_sent'; public const string MESSAGE_ALREADY_PROCESSING = 'message_already_processing'; diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php index 70781f41af..1da9196980 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php @@ -14,6 +14,7 @@ use Appwrite\Utopia\Response; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Helpers\ID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -136,7 +137,7 @@ class Create extends Base throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'When connecting to VCS (Version Control System), you need to provide "installationId" and "providerBranch".'); } - $site = $dbForProject->createDocument('sites', new Document([ + $site = new Document([ '$id' => $siteId, 'enabled' => $enabled, 'live' => true, @@ -166,13 +167,17 @@ class Create extends Base 'runtimeSpecification' => $specification, 'buildRuntime' => $buildRuntime, 'adapter' => $adapter, - ])); + ]); + + try { + $site = $dbForProject->createDocument('sites', $site); + } catch (DuplicateException) { + throw new Exception(Exception::SITE_ALREADY_EXISTS); + } - // Git connect logic if (!empty($providerRepositoryId)) { $teamId = $project->getAttribute('teamId', ''); - - $repository = $dbForPlatform->createDocument('repositories', new Document([ + $repository = new Document([ '$id' => ID::unique(), '$permissions' => $this->getPermissions($teamId, $project->getId()), 'installationId' => $installation->getId(), @@ -184,8 +189,8 @@ class Create extends Base 'resourceInternalId' => $site->getSequence(), 'resourceType' => 'site', 'providerPullRequestIds' => [] - ])); - + ]); + $repository = $dbForPlatform->createDocument('repositories', $repository); $site->setAttribute('repositoryId', $repository->getId()); $site->setAttribute('repositoryInternalId', $repository->getSequence()); } diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php index 642d6bbe91..895e7ae3ef 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php @@ -190,11 +190,9 @@ class Update extends Base $repositoryInternalId = ''; } - // Git connect logic if (!$isConnected && !empty($providerRepositoryId)) { $teamId = $project->getAttribute('teamId', ''); - - $repository = $dbForPlatform->createDocument('repositories', new Document([ + $repository = new Document([ '$id' => ID::unique(), '$permissions' => $this->getPermissions($teamId, $project->getId()), 'installationId' => $installation->getId(), @@ -206,8 +204,8 @@ class Update extends Base 'resourceInternalId' => $site->getSequence(), 'resourceType' => 'site', 'providerPullRequestIds' => [] - ])); - + ]); + $repository = $dbForPlatform->createDocument('repositories', $repository); $repositoryId = $repository->getId(); $repositoryInternalId = $repository->getSequence(); } diff --git a/tests/unit/Migration/MigrationTest.php b/tests/unit/Migration/MigrationTest.php new file mode 100644 index 0000000000..bb6c49d2fc --- /dev/null +++ b/tests/unit/Migration/MigrationTest.php @@ -0,0 +1,51 @@ +method->invokeArgs($this->migration, [ + $this->method->invokeArgs($this->migration, [$document]) + ]); + } + + /** + * Check versions array integrity. + */ + public function testMigrationVersions(): void + { + require_once __DIR__ . '/../../../app/init.php'; + + foreach (Migration::$versions as $class) { + $this->assertTrue(class_exists('Appwrite\\Migration\\Version\\' . $class)); + } + + // Test if current version exists + // Only test official releases - skip if latest is release candidate + if (!(\str_contains(APP_VERSION_STABLE, 'RC'))) { + $this->assertArrayHasKey(APP_VERSION_STABLE, Migration::$versions); + } + } +}